CLEAN CODE (principles, approaches)

1. What is clean code

Чистый код - код, который следует следующим принципам:

  • Все переменные и названия функций (классов, методов) имеют четкие названия, которые точно описывают что они делают и за что отвечают.

  • Чистый код должен выполнять одну задачу. Каждая функция должна выполнять только одно задание, которое четко формируется в ее названии. Класс должен содержать только те поля и методы которые нужны только для него. Все лишнее нужно выносить в отдельные сущности. Чистый код содержит минимум классов и других движущихся частей

  • Должен быть легко читаем, прост и понятен. Чистый код легче и дешевле поддерживать.

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

  • Код должен содержать полную обработку ошибок.

  • Не должен содержать дубликаты

  • Код должен быть написан в одном стиле. Нужно создать Code Convention и придерживаться его. Это особенно важно, если над ним работает команда

  • Минимум комментариев в коде. Он должен быть самодокументованным, ведь комментарии не компенсируют плохой код

Чистый код достижим, но стремление к нему бесконечно.

2. What is code smells

Code smells (код с "запашком") - любые признаки в исходном коде программы, которые указывают на наличие глубоких проблем. Это некий набор общих признаков, которые указывают на то, что код недостаточно хорош, и для его чистоты потребуется рефакторинг:

  • дублированный код и логика

  • неправильный, нечеткий нейминг

  • длинные методы и классы. Если вам нужна такая проверка, то создайте для нее собственный метод. Идеальный метод должен состоять из 4–20 строк. Если строк больше, то извлеките часть их них в новый метод. То же касается и классов: чем меньше, тем лучше. Особенно, если вы придерживаетесь принципа единственной ответственности.

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

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

  • стрельба дробью. Это антипаттерн в разработке ПО. При выполнении любых модификаций приходится вносить множество мелких изменений в большое число классов. «Стрельба дробью» похожа на «Расходящуюся модификацию», но является её противоположностью. Расходящаяся модификация имеет место, когда есть один класс, в котором производится много различных изменений, а «Стрельба дробью» — это одно изменение, затрагивающее много классов.

  • наличие "завистливых" функций. Метод обращается к данным другого объекта чаще, чем к собственным данным.

  • не следование KISS, DRY, YAGN, SOLID.

3. What is anti patterns

Это умение распознать контрпродуктивные решения в дизайне, коде, процессах и поведении. Знание о вредных шаблонах решений полезно

Copy and Paste Programming

Самый махровый из всех анти-паттернов.

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

Какие проблемы несёт

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

  2. Понижается качество кода — часто найденные недочёты в коде правятся только в одном проекте, в остальных недостатки остаются.

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

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

Boat anchor

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

Когда он проявляется

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

  2. При сохранении части кода «на будущее», на случай, если придётся ещё раз использовать.

Какие проблемы несёт Значительно усложняет чтение проекта, не неся абсолютно никакой практической ценности.

Hard code

Хард-код — фиксация в коде различных данных об окружении.

Например: пути к файлам, имена процессов, устройств и так далее.

Когда он проявляется Этот анти-паттерн тесно связан с магическими числами, часто они переплетаются.

Какие проблемы несёт

  1. Код будет исправно работать только в окружении, в котором ведётся разработка.

  2. Может проявляется непредсказуемые дефекты во время перемещения, переименования файлов, и их поведение может меняться при изменения конфигурации устройств.

Soft code

Софт-код — параноидальная боязнь хард-кода. Этот анти-паттерн является вторым концом палки о хард-коде и поэтому тоже является опасным.

Когда он проявляется В проекте настраивается абсолютно всё, что делает конфигурацию невероятно сложной и непрозрачной.

Какие проблемы несёт

  1. При разработке много ресурсов уходит на реализацию возможности настроек абсолютно всего.

  2. Развёртывание такой системы влечет также дополнительные затраты.

Золотой молоток

уверенность в полной универсальности кода.

Когда он проявляется Применение одного решения (чаще всего какого-либо одного шаблона проектирования) для всех задач.

Какие проблемы несёт Многие программисты используют данный анти-паттерн не подозревая о собственной некомпетентности, что приводит к остановке саморазвития.

Магические числа

Это числовые значения, встречающиеся в коде, но при этом неочевидно, что они означают. Данный антипаттерн затрудняет понимание программы и усложняет её рефакторинг.

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

Плюсы:

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

  • Значение константы намного проще заменить, чем искать нужное число по всему коду, при этом рискуя заменить такое же число, которое в данном конкретном случае использовалось для других целей.

Преждевременная оптимизация

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

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

В чём сложность Сложность в том, чтобы знать, когда оптимизация будет преждевременной. Важно заранее оставлять место для роста. Нужно выбирать решения и платформы, которые позволят легко оптимизировать и расти. Также иногда можно использовать преждевременную оптимизацию в качестве оправдания за плохой код. Например, использование алгоритма O(n2) просто потому, что алгоритм O(n) сложнее.

Класс Бога

Классы, контролирующие множество других классов, имеющие много зависимостей и много ответственности.

В чем сложность Проекты, запросы и количество программистов растет, и маленькие специализированные классы медленно превращаются в классы Бога. Рефакторинг таких классов может занять впоследствии много времени.

Как избежать Разбивайте ответственность по мелким классам, с единственной ответственностью, которая чётко определена, юнит-тестируется и задокументирована.

Страх перед добавлением классов

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

В чем сложность Добавление классов уменьшает сложность. Распутать несколько мелких клубков пряжи проще, чем один крупный. Несколько простых в поддержке и документировании классов предпочтительнее одного большого и сложного класса со множеством зависимостей (класс Бога). Добавление классов – не панацея. Упрощение дизайна разбиванием больших классов требует глубокого анализа областей ответственности и требований.

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

Бесполезные (полтергейстные) классы

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

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

Как избежать Не пишите бесполезные классы и избавляйтесь от них при возможности.

More info:

4. Best practices (KISS, DRY, YAGNI). Good Software principles

DRY (Don’t Repeat Yourself)

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

Избегать повторения. If some code fragment is duplicated in several places inside a program, there is a high probability of two catastrophic situations:

  1. When making even small changes to the source code, you need to change the same code in several places. It will require additional time, effort and attention(often it is not easy).

  2. The first item follows the second one. You or another developer from your team may accidentally miss one of the changes(it can happen simply by merging branches in vcs) and face the subsequent bugs in the application. These bugs can be frustrating to you because you have heard that such a bug has already been fixed.

  3. увеличение кода => увеличение бандлов => perfomans => user experience => отклик сайта => юзер 5 сек на стр находится. Удержание юзеров

KISS (Keep it simple, stupid!)

Не усложняй, тупица. это принцип проектирования и программирования, при котором простота системы декларируется в качестве основной цели или ценности

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

This principle says that the code must be as simple as possible without complex structures, otherwise it will complicate debugging and maintenance of the code. Besides, it will be more difficult for another programmer to understand the code's logic, which in its turn will also require additional time and effort.

By doing this, you will make life easier for yourself and your colleagues, because complexity generates bugs.

+ ускоренный онбоардинг =>

YAGNI (You aren’t going to need it)

_ это процесс и принцип проектирования ПО, при котором в качестве основной цели и/или ценности декларируется отказ от избыточной функциональности, — то есть отказ добавления функциональности, в которой нет непосредственной надобности.

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

The desire to implement at once all the necessary (and sometimes even unnecessary) functionality from the very beginning of the project. That is, when a developer adds all the possible methods to the class from the very beginning and implements them, and may even never use them in the future. Thus, according to this recommendation, first of all, implement only what you need, and later, if necessary, extend the functionality.

Code review

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

Процесс - самостоятельно => команда + ревьюверы со стороны заказчиков => prod. Code review’s purpose is to find and fix overlooked mistakes and to improve the overall quality and security of software. Advantages:

  • Members of your team can learn from each other by reviewing each other's code.

  • A general analysis report says that if you review at an earlier stage, the cost to fix this will be lower and thus it will reduce the overall cost of the application.

  • Code reviews will decrease the number of those bugs that make it to production.

  • Things like readability, efficiency, and maintainability of your code may not always directly affect your users but they are tremendously important for your project in the long run.

  • Code reviews also make it easy to spot potential vulnerabilities and to fix them before they make their way to your servers.

The following points should be checked:

  • Readability

  • Functional correctness

  • Completeness

  • Hidden implications

  • Coding standards, code guide (arnbn)

  • Tests

SOLID

Solid - один из подходов написания чистого и доступного кода, представленный 5-ю методами(принципами).

Solid используется в модулях. Модулем мы будем называть какую-то часть кода, обособленную от других. Это может быть класс, функция, объект, файл — в общем, что-то, у чего есть границы, отделяющие этот код от другого.

S-Принцип единственной ответственности

Каждый класс должен решать лишь одну задачу. Означает, что у модуля должна быть только одна причина для изменения.

Основной инструмент принципа — объединять те части, которые меняются по одной причине, и разделять те, которые меняются по разным.

Принцип единой ответственности:

  • помогает разбивать и декомпозировать задачи по одной на модуль;

  • уменьшает количество модулей, которые надо изменить при изменении требований;

  • ограничивает влияние изменений, помогая контролировать сложность системы.

O-Принцип открытости-закрытости

Программные сущности (классы, модули, функции) должны быть открыты для расширения, но не для модификации.

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

Основная цель принципа — помочь разработать проект, устойчивый к изменениям, срок жизни которых превышает срок существования первой версии проекта.

Принцип открытости-закрытости:

  • заставляет проектировать модули так, чтобы они делали только одну вещь и делали её хорошо;

  • побуждает связывать сущности через абстракции (а не реализацию) там, где могут поменяться бизнес-требования;

  • обращает внимание проектировщиков на места стыка и взаимодействие сущностей;

  • позволяет сократить количество кода, который необходимо менять при изменении бизнес-требований;

  • делает внесение изменений безопасным и относительно дешёвым.

L-Принцип подстановки Барбары Лисков

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

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

Наследуемые объекты должны вести себя также, как и родительские (поведенческая совместимость).

I-Принцип разделения интерфейса

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

Сущности не должны зависеть от интерфейсов, которые они не используют.

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

ISP помогает проектировать интерфейсы так, чтобы изменения затрагивали только те модули, на функциональность которых они действительно влияют. Чаще всего это заставляет интерфейсы дробить (разделять).

В js нет интерфейсов, есть контракты.

Принцип разделения интерфейса:

  • помогает бороться с наследованием или реализацией ненужной функциональности;

  • даёт возможность спроектировать модули так, чтобы их затрагивали изменения только тех интерфейсов, которые они действительно реализуют;

  • снижает сцепление модулей;

  • уничтожает наследование ради наследования, поощряет использование композиции;

  • позволяет выявлять более высокие абстракции и находить неочевидные связи между сущностями.

Подходы: Разделение через делегирование

Этот подход подразумевает использование шаблона проектирования под названием Адаптер. Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.

Мы не будем связывать SoundEmitter и TimeInterval непосредственно друг с другом. Мы будем связывать их через дополнительный слой (адаптер), который будет транслировать сообщения от одной сущности другой.

Разделение через множественное наследование Второй вариант предполагает, что IntervalSoundEmitter будет наследоваться одновременно от TimeInterval и от SoundEmitter. Это позволит отвязать родительские классы друг от друга и использовать в классе IntervalSoundEmitter только нужную функциональность.

D-Принцип инверсии зависимостей

Принцип инверсии зависимостей предполагает, что:

  • Высокоуровневые модули не должны зависеть от низкоуровневых; оба типа должны зависеть от абстракций.

  • Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций(прослойки в виде интерфейса).

Принцип инверсии зависимостей:

  • вводит правила и ограничения для зависимости одних модулей от других;

  • снижает сцепление модулей;

  • делает тестирование модулей проще;

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

Когда модули жёстко сцеплены, они слишком много знают друг о друге и не функционируют по отдельности. В такой ситуации изменения в одном будут требовать изменений в других — что нарушает OCP.

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

Связность — степень, в которой задачи некоторого модуля, связаны друг с другом. Чем выше связность, тем строже модули следуют SRP, тем выше сфокусирован модуль на конкретной задаче.

Last updated