Всем доброе время суток, я занимаюсь программированием уже 8 лет, причем программированием во всех его проявлениях: разработка серверной части на разных языках программирования, разработка клиентской части, программирование сетей, администрирование linux, оптимизации производительности. И когда такое долгое время занимаешь каким-либо делом, искренне его любишь и стремишься к саморазвитию, то неизбежно начинают прорисовываться различные причинно-следственные связи в соответствующей области, ты начинаешь видеть те взаимосвязи, о которых раньше даже не догадывался или попросту считал их незначительными, это собственно и является тем, что люди называют опытом.
И именно об одной из таких причинно-следственных связей я бы и хотел с вами поделиться в этой статье. Я думал написать, что это одна из самых важных причинно-следственных связей для того, что бы быть чуть менее радикальным, но все таки нет, эта причинно-следственная связь является самой важной причинно-следственной связью в программировании с огромным отрывом. Она проходит красной нитью сквозь абсолютно все области программирования: написание кода, проектирование интерфейса, развертывание приложения, организация работы команды, ведение баг трекера, и так далее. И эту сакральную причинно-следственную связь я называю минимизация переменных.
Под термином переменная я подразумеваю не только обычные переменные (которые объявляются через ключевые слова var, let, function), но так же и переменные в более абстрактном смысле этого слова: созданные папки в дереве проекта, подключаемы библиотеки, количество ajax вызовов на сервер, количество компонентов в интерфейсе, количество протоколов которым должен следовать программист при коммите кода (например code review, каждая фича в отдельном бранче) и так далее.
По факту, весь процесс программирования — это перебор вариативностей в направлении того, как реализовать тот или иной функционал используя максимально минимальное количество переменных (по крайней мере я смотрю на программирование именно с такой перспективы). Тот код, который реализовывает фичу меньшим количеством зависимостей / файлов / строчек кода лучше, чем тот, который реализовывает тот же самый функционал большим количество переменных. Если утилитарный класс содержит только одну функцию — нужно избавиться от этого утилитарного класса, если функция состоит только из одной строчки — нужно стоит избавиться от этой функции, если в компоненте находится только 10 строчек кода — нужно удалить этот компонент, если в папке только один файл — нужно избавиться от этой папки, и так далее. И когда несколько тысяч таких, казалось бы незначительных изменений складываются вместе, то в результате получается красивый, лаконичный и минималистичный код, который очень просто читать. Дьявол кроется в деталях как говорится.
Вот список положительных эффектов, которые проявляются когда вы начинаете программировать отталкиваясь от принципа минимизации переменных:
- Меньше багов. Так как пишется меньшее количество кода, то количество мест где можно допустить ошибку соответственно так же уменьшается.
- Легче вводить новых людей в проект. Сложность проекта прямо пропорциональна количеству переменных в этом проекте, чем меньше переменных, тем проще вводить новых людей.
- Проще добавлять новый функционал. Так как нужно изучить меньшее количество кода, то проще становится «загружать» функционал в голову, и как следствие добавлять новый функционал или изменять существующий код.
- Предсказуемость. Процесс программирования становится значительно более предсказуемым, вам становится проще предсказать сколько времени займет разработка той или иной фичи за счет того, что уменьшается количество «затыков». Обычно именно затыки и вызывают самое большое количество проблем, и ведут к тому затягиванию дедлайнов.
Но очень часто происходит обратная ситуация, программисты начинают самоутверждаться за счет введения новых переменных. Вводится 20 различных библиотек, CSS-фреймворков, создается огромное количество многоуровневых абстракций по типу UserFactoryManager (особенно этим грешат Java программисты по какой-то причине), создается слишком большое количество директорий, вводятся более продвинутые методологии разработки, добавляется большее количество протоколов перед коммитом файлов, и так далее. И мотивацию стоящую за такими решениями можно понять, ведь это создает иллюзию профессионализма, дескать посмотрите как много всего я знаю, и какими сложными абстракциями я умею оперировать. Но зачастую это причиняет чудовищный вред проекту.
Не поймите меня неправильно, я не против применения более продвинутых методологий, фреймворков и технологий я просто говорю о том, что очень часто они вводятся просто из-за хайпа, а не потому что эта технология решает какую-либо насущную проблему в проекте.
Решение проблемы слишком большим количествов переменных можно назвать Overengineering. Но согласитесь, что если описывать эту ситуацию как то, что проблема решается слишком большим количеством переменных, является куда более точным описанием данного явления.
Зачастую введение новых переменных оправдывается масштабируемостью, дескать если тут создать UserManagerFactory, то потом мы сможем в любой момент времени взять и добавить функцию, если возникнет необходимость. Но в действительности масштабируемость это не когда вводится много переменных, а наоборот когда переменных мало. Подумайте, где для вас будет проще писать новый функционал: в движке браузера chrome или в совершенно новом проекте, который вы пишете с нуля? Введение новых переменных не ведет к масштабируемости архитектуры. То что действительно улучшает масштабируемость проекта, это когда вы удаляете ненужные переменные и упрощаете существующий код.
Огромное количество лишних переменных — это именно та причина по которой я лютой ненавистью ненавижу язык программирования Scala, и та причина по которой я люблю JavaScript. Scala — это просто квинтэссенция переусложненная, очень запутанная система типов, множество концептов которые дублируют друг друга, неявные преобразования, огромное количество способов сделать одно и то же. Когда я использую этот язык программирования, то я думаю не о том что нужно сделать, а о том как это сделать. Я очень часто ловлю себя на том, что просто сейчас моя цель просто заткнуть компилятор. С другой стороны JavaScript — это полная противоположность Scala, он на порядок минималистичнее, используя его я трачу значительно меньшее количество ментальных усилий для того, что бы выразить тот или иной концепт. Безусловно в такой простоте имеются и свои минусы, например в Scala компилятор сообщает об ошибке на этапе компиляции, в то время как в JavaScript об ошибке можно узнать только в рантайме и так же для JavaScript принципиально невозможно написать настолько же функциональную IDE как для строго-типизированных языков, но это те жертвы с которыми я готов смириться.
Принцип минимизации переменных необходимо применять и при проектировании интерфейсов, для примера давайте сравним веб-интерфейсы для двух конкурирующих социальных сетей для знакомств Tinder и Badoo. В веб-интерфейсе Tinder по факту существует только одна страница, профиль пользователя, активные чаты и поиск новых знакомств отображаются на одной странице и отсутствует необходимость перехода на другие страницы, в этом интерфейсе все потребности пользователя удовлетворяются минимальным количеством компонентов. В то время как на Badoo функционал перехода на профиль пользователя, интерфейс сообщений и страница для поиска пар реализованы как отдельные страницы, пользователю необходимо делать большее количество действий для удовлетворения потребностей, и соответственно этот интерфейс является менее эффективным. Конечно это всего лишь один очень небольшой пример, но когда таких примеров становятся десятки и сотни, то все вместе они и определяют то, является ли интерфейс продуманным или нет. При проектировании интерфейса необходимо создавать минимальное количество компонентов для того, что бы удовлетворить потребности пользователя.
По этой же причине мне совершенно не нравятся подходы, которые разделяют логику, данные и представление, потому что в этом случае получается ситуация при которой для написания одного кусочка функционала необходимо создать три файла, потом количество компонентов начинает расти, и когда проект уже стал большим, то количество файлов начинает зашкаливать за все разумные пределы. Например при написании веб-интерфейсов, стили обычно описываются в отдельных файлах, в то время как ноды, к которым эти стили прикрепляются, находятся в другом файле. Основная мотивация такого разделения, как ни странно, это масштабирование, дескать если разделить сами ноды и их стили, то тогда станет проще ориентироваться в проекте, и можно будет независимо разрабатывать эти две части функционала. Но давайте посмотрим какие переменные добавляются при таком подходе: новые файлы со стилями для каждого компонента, селекторы (они описывают то к какой ноде прикрепляются стили), правила каскадирований (если несколько селекторов адресуют одну и ту же ноду). В то время как если писать стили прямо в тех нодах, к которым эти стили должны подключаться (например через style аттрибут), то тогда отпадает необходимость во всех этих дополнительных переменных, что сильно достаточно сильно упрощает код. В последнее время такой подход начинает становиться все более популярным, появляется множество библиотек, которые позволяют писать стили напрямую в аттрибуте style.
Новые переменные должны вводится в проект всегда постепенно. Например в самом начале при написании проекта у меня все файлы находятся в рутовой директории, вообще без каких-либо субдиректорий, просто потому что, так проще, и только когда количество файлов разрастается до 40-60 файлов, некоторые файлы группируются в директории. В самом начале не нужна никакая методология разработки, просто закиньте всех программистов в кучу, дайте им задачи, дальше они сами разберутся, если слишком рано вводить методологию разработки, то она будет ощущаться как какой-то суррогатный концепт. Когда переменные вводятся до того, как в них возникла необходимость, то такое называется преждевременной оптимизацией.
Точно так же минимизировать переменные необходимо и при написании документации / статей, когда я пишу документацию то я все время думаю о том, какое именно минимальное количество предложений мне необходимо, для того, что бы передать свою мысль. Я буквально анализирую каждое предложение, и если мне кажется, что в этом предложении нету необходимости, оно не несет никакой дополнительной смысловой нагрузки, то оно удаляется, в результате получается очень сжатая страница с документацией, которая содержит максимальное количество необходимой информации в минимальном объеме текста.
По факту у вообще любой переменной есть презумпция виновности, никакая переменная не нужна до тех пор пока не доказано обратное.
Вы можете мне возразить, дескать то же мне, открыл Америку, это уже давным-давно известно и описывается такими принципами как: Бритва Окама, SOLID. Но мне больше нравится называть этот концепт именно «минимизации переменных», просто потому что это такая интерпретация очень лаконично ложится в голову и соответственно ее намного проще придерживаться на практике. Кто из вас может похвастаться тем, что помнит каждый пункт из принципа SOLID?
Новички пишут простой код, потому что не умеют писать сложный, средние программисты пишут сложный код, просто потому что могут, профессионалы пишут простой код, потому что не хотят писать сложный.
При этом необходимо помнить, что любая крайность это плохо: если фанатично минимизировать переменные, прямо до атомарного уровня, то такой подход, так же как и введение слишком большого количества переменных является крайностью. А любая крайность как известно это плохо, истина всегда находится где-то посередине.
Автор: mnemosha