4.4 Что такое грязный код

В этом параграфе мы расскажем, как код может быть чистым или грязным, кто это придумал и от чего зависит чистота.

Но прежде чем начать — небольшой дисклеймер.

Этот параграф получился довольно «философским».

Он будет интересен тем, у кого есть опыт в программировании.

Чистый код

Проходит время, программист учится, развиваются его навыки. От примитивного Hello World и учебных проектов он переходит к написанию простых программ, а затем и полноценных приложений, которые содержат множество строк кода, собранных в сотни и тысячи файлов.

Всё большее значение начинает играть не только написание рабочего кода, но и его понятность для других программистов, как и для самого себя в будущем. На этом пути любой программист сталкивается с разными акронимами, принципами программирования и наверняка приходит к понятию «чистый код».

Сам термин «чистый код» (Clean Code) популяризировал Роберт Мартин (Дядюшка Боб) в одноимённой книге, где он рассматривал примеры рефакторинга, улучшения читаемости кода.

Что за Дядюшка Боб?

Это американский инженер, программист и консультант в области разработки программ.

Разработчик программ с 1970 года. С 1990-го — международный консультант в области гибких методик разработки программ. Один из авторов манифеста гибкой разработки (Agile Manifesto).

Роберт Мартин. Источник

Сам Дядюшка Боб говорил про чистый код так: «Сколько программистов, столько и определений».

Мы будем использовать такое:

Чистый код — это код, который легко читать, поддерживать, расширять и тестировать.

Книга Мартина стала бестселлером со своей первой публикации 1 августа 2008 года. В ней много примеров, контрпримеров, она популяризировала много подходов исправления кода (рефакторинга).

Впрочем, есть и недостаток: она так и не отвечает на вопрос, как писать чистый код. Эта книга породила целую школу программистов, которые портят программы в погоне за чистым кодом.

Например, Дэн Абрамов в своей статье «Прощай, чистый код» советует: «Не будьте фанатиком чистого кода. Чистый код — это не цель».

Кто такой Дэн Абрамов?

Российский разработчик из Facebook, член команды React Core, соавтор библиотеки Redux и утилиты Create React App.

Когда мы ставим во главу угла «читаемость», возможность дополнения кода новыми сущностями и функциями («расширяемость» кода), а не исполнение им своей задачи, то получаем в итоге красивую программу, которая перестаёт быть программой.

Любая программа пишется не столько для чтения людьми, сколько для чтения и исполнения машиной. Увлекаясь приведением кода в порядок (рефакторингом), мы рискуем забыть о главной цели: код должен работать, исполнять свою функцию.

Противопоставления «чистый, но нерабочий код» и «грязный, но рабочий» на самом деле не противопоставления. Это две крайности, каждая из которых вредит программе и программистам.

Программирование и цифровая революция похожи на промышленное производство и индустриальную революцию прошлого. Согласитесь, сложно представить производство без грязи, копоти и физического напряжения. Также сложно представить себе программирование без грязного кода и интеллектуального напряжения.

При этом важно поддерживать рабочее место на производстве в чистоте, а также проводить постоянный рефакторинг и улучшение читаемости кода в программировании. Как на производстве это часть безопасности труда и трудовой дисциплины, так и в программировании это повышает безопасность исполнения программ.

Знаем, очень много абстракций и рассуждений. Давайте рассмотрим работу с кодом на примере.

Пример

Представим, что мы написали функцию с магическими числами или магическими переменными.

1
2var myMap = new ymaps.Map("map", {
3            center: [20, 37],
4            zoom: 5
5        }, {
6            searchControlProvider: 'yandex#search'
7        });
8
9function addPoint(m , n){
10
11var g =  new ymaps.GeoObject({
12            geometry: {
13                type: "Point",
14                coordinates: [m, n]
15            }})
16
17myMap.geoObjects.add(g)
18
19}
20
21addPoint (30, 25);
22

Не надо тут же бросаться исправлять имена переменным, писать комментарии к каждой строчке. Сначала важно проверить, работает ли программа, выполняет ли своё назначение.

Только после положительного ответа имеет смысл исправлять названия переменных на более привычные или добавлять комментарий, который разъяснит их назначение.

1...
2
3// m — широта n — долгота
4function addPoint(m , n){
5
6var point =  new ymaps.GeoObject({
7            geometry: {
8                type: "Point",
9                coordinates: [m, n]
10            }})
11
12myMap.geoObjects.add(point)
13
14}
15
16...
17

В примере выше код выполняет своё назначение, он прост и короток, а из названий функций сразу становится понятна их задача.

Грязный код

Плохой код, от которого бегут все программисты, называется «спагетти-код», а плохо структурированные модули — «равиоли». Именно с ними нас призывает бороться книга «Идеальный код».

Существует множество примеров антипаттернов и плохого кода, чаще всего их называют как блюда итальянской кухни: спагетти-код, пицца-код, лазанья-код, равиоли-код и так далее.

Эти названия появились из-за сходств с блюдами: спагетти-код так же спутан, равиоли-код состоит из большого количества отдельных элементов, лазанья-код — из неразделимых слоёв.

3.3

Сейчас покажем на примерах, как выглядят «спагетти», «лазанья» и «равиоли». Тут же проведём рефакторинг и объясним, почему новая версия лучше.

Спагетти-код

Это неструктурированный, запутанный код, в котором трудно проследить логику из-за большого количества вложенностей, переходов, повторений и отсутствия модульности. Его сложно читать, тестировать и поддерживать.

Например, функция ниже что-то делает с данными. Мы не можем с первого взгляда сказать, что именно.

3.3

Поэтому вынесем описательную логику в отдельную функцию describe_number, чтобы улучшить читаемость, избавиться от вложенности и упростить тестирование.

Такой код легче расширять и поддерживать в будущем — и это цель любого рефакторинга.

Лазанья-код

Это код с чрезмерным количеством уровней абстракции и вложенности, где каждый слой оборачивает другой. Сложно понять, где происходит реальная работа, потому что логика размазана по множеству уровней функций или классов.

В примере ниже — каждая функция вроде бы делает полезную вещь, но, чтобы понять, откуда взялся response, нужно пройти восемь уровней. Такой код часто выглядит «архитектурно чистым», но он непрозрачен, особенно если логика размазана без нужды.

3.3

Мы сократили цепочку вызовов и ужали большую часть логики в одной функции. Такой код проще читать и расширять.

Единственное — выделили функциональность, отвечающую за поиск пользовательского ID в токене и обращение к БД. Их лучше держать отдельно.

Равиоли-код

Это противоположность спагетти-кода: код состоит из множества маленьких изолированных модулей или функций, каждая из которых сама по себе ясна, но в целом сложно понять, как они взаимодействуют.

Проблема — не в сложности функции, а в сложности связей между ними.

В примере ниже каждая функция делает очень мало. Она не связана с другими функциями (и это отличие от лазанья-кода), но у нас всё равно получается цепочка зависимостей, которая мешает проследить общую картину.

3.3

Решение — упростить.

Но как и с лазанья-кодом, что-то мы оставим в отдельных функциях (например, получение пользователя из БД).


Вот и всё!

Напоследок — два напутствия:

  • Код может быть грязным, но не от любого грязного кода нужно бежать.
  • Код стоит исправлять до тех пор, пока он не станет рабочим и понятным для вас и ваших коллег. Это так же важно, как поддержание порядка на рабочем месте.

А ещё совет «на вырост»: решить проблемы с грязным кодом очень хорошо помогает знание архитектурных паттернов и шаблонов проектирования.

Первые определяют структуру всего проекта, вторые предлагают решение частых проблем на уровне кода. Но это разговор на отдельный параграф, пока просто имейте в виду, что такие штуки существуют.

В следующем параграфе поговорим, откуда в программах берутся ошибки и кто защищает нас от них.

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф4.3. Что такое системы контроля версий и зачем они нужны: на примере Git
Следующий параграф4.5. Откуда в программах берутся ошибки