Общие правила
При внесении изменений и дополнении существующего кода пиши уместный код. Изменённый или добавленный код не должен выглядеть чужеродным наростом. Даже если это требует нарушения всех остальных императивов.
Не скрывай симптомы проблемы вместо устранения её причины. Если установленную причину нельзя устранить в разрешённой области, сообщи о ней и остановись.
Пиши разумный код. Код, будучи превращённым в предложения на английском языке с сохранением смысла и структуры должен соответствовать здравому смыслу.
Пиши единообразный код. Подобный код, должен выглядеть и делать подобные вещи и ощущаться подобно. Схожие задачи должны решаться схожим образом и схожим средством. Явные и не явные списки и коллекции должны состоять из однородных элементов, с однородными названиями и оформлением.
Не повторяйся. Исключай повторы одной и той же идеи как внутри добавленного кода, так и между добавленным и существующим кодом. Опирайся не только на синтаксическую похожесть, но и на семантическую — одну и ту же мысль, выраженную по разному или разными средствами так же надо стараться дедуплицировать. Но не выполняй дедупликацию механически, если для идеи не удаётся подобрать точного, но универсального имени — скорее всего её не стоит пытаться дедуплицировать.
Пиши линейный код. Если есть возможность переделать модель или требования к вызову и за счёт этого убрать ветвление — делай это. Если специальный случай или ошибка возможна только теоретически, а последствия отсутствия обработки можно устранить — получи подтверждение у заказчика работы и убери её обработку из кода.
Не пиши лишнего кода. Если код можно удалить и тесты продолжать проходить — удали его. Если для интерфейса не ожидается двух и более реализаций в Код системы, реализующий её поведение, в отличие от тестового кода и тестовой инфраструктуры. Подробнее — удали его.
Не допускай циклы в зависимостях. Ни в модели данных, ни между классами, ни между пакетами, ни между сервисами.
Делай структуру кода оглавлением системы. Выделяй значимые части системы в отдельные файлы и каталоги. Их имена и расположение должны показывать, с какими понятиями работает система и какие возможности предоставляет. Разбивай крупные части дальше так, чтобы нужную реализацию можно было найти, последовательно раскрывая это оглавление.
Пиши в прагматичном функциональном стиле. В основе кода используй неизменяемые данные и функции, результат которых зависит только от аргументов. Предпочитай функциональные map/filter, но допускай императивные for-ы, локальную мутацию и исключения, когда они делают код понятнее или производительнее. Используй reduce, flatMap и т. п. операции с аккумулятором с осторожностью — только если обычный цикл не будет более простым для чтения. Не допускай выхода изменяемого состояния за границы Операция, которая при одинаковых входных данных возвращает одинаковый результат и не оказывает внешних эффектов. Подробнее; в частности не используй изменяемые типы в качестве параметров и результатов вычисления. При необходимости, вычисления могут писать служебные логи в консоль.
Соблюдай принцип ямы успеха (Pit of Success). API должно быть проще использовать правильно, чем не правильно.
Дизайн функций
Соблюдай принцип разделения команд и запросов (Command-Query Segregation principle) Операция, результат или эффект которой зависит от момента выполнения или числа вызовов. Подробнее должны быть либо Действие, предназначенное для изменения состояния системы. Может возвращать данные, сформированные при выполнении, но не должна возвращать данные, которые сущестовали до её выполнения. Подробнее, либо Действие, предназначенное для получения сведений о состоянии системы или выводов на их основе. Должен быть безопастным — запрос не должен менять состояние системы, поэтому его должно быть возможно вызывать несколько раз не опосаясь нежелательных побочных эффектов. Подробнее.
Пиши простые действия. Выноси обработку данных и сложные проверки в вычисления, независимые от IO. Сохраняй в самом действии явный поток управления: порядок вызовов, ветвления и возможные исходы, включая досрочное завершение и прерывание с ошибкой. Когнитивная сложность каждого действия не должна превышать 4. Предпочитай защитные выражения (guard clause) повешению глубины вложенности кода функции.
Избегай неявной временной связанности (temporal coupling) Если один метод должен быть вызван после другого, то он должен получать на вход параметр, значение для которого проще всего получить вызвав первый метод
Держи управление жизненным циклом ресурса в одной функции. Закрытие ресурса (файл, подключения, блокировки, потока и т.п.) должно быть в той же функции, что и открытие.
Реализация функции должна быть выражена в терминах максимум двух доменов. Не смешивай в теле одной функции термины из HTTP, SQL и предметной области продукта. Единственное исключение - функции конвертации, которые могу использовать термины двух смежных доменов.
Дизайн классов
Сохраняй уровень абстракции API класса. Всё API класса должно быть выражено в терминах одного словаря — предметной области,
Следи за тем, чтобы детали реализации класса не упоминались в его API — как в именах методов и параметров, так и в типах параметров и возвращаемых значений.
Пиши связные классы. Стремись к тому, чтобы каждый метод использовал каждое поле класса.
Дизайн структур данных
Пиши связные структуры данных. Объединяй в структуру поля, описывающие одно понятие. Имя структуры должно выражать это понятие. Если у понятия есть несколько вариантов с разными атрибутами — они должны быть описаны разными структурами данных.
Не допускай функциональных зависимостей между полями. Допустимость значения одного поля не должна зависеть от значения другого.
Делай невалидные состояния непредставимыми. Не допускай возможности создать объект или структуру, представляющие невалидное состояние.