Архитектура и организация кода
Решение о том, где место конкретному куску кода, что ему позволено знать и где проходят швы. Речь не столько о схемах, сколько о границах, которые кодовая база удерживает, когда её два года правят шесть человек.
Почему это важно. Архитектура редко ломается громко. Она ломается как медленный рост цены каждого изменения — пока правка на одну строку не начинает задевать девять файлов и никто уже не может объяснить почему.
Что нужно понимать
- За что отвечает этот кусок кода — одним предложением
- От чего ему разрешено зависеть и что не должно зависеть от него никогда
- Какие решения дорого откатывать, а какие нет
- Окупается ли абстракция или просто добавляет ещё один слой
- Куда полезет новый человек по интуиции и вознаграждает ли структура эту догадку
Основные темы
Ответственность и границы
Единица кода делает одну вещь и честно её называет.
- Единственная ответственность — на практике, а не как лозунг
- Публичная поверхность против внутренностей
- Сколько должен стоить переход через границу
Зависимости
Направление важнее количества.
- Направление зависимостей и почему оно должно быть односторонним
- Инверсия — зависеть от интерфейса, а не от реализации
- Циклы и почему они первый признак того, что где-то не хватает границы
Структура
- Feature-first против layer-first и что каждый из подходов оптимизирует
- Где живёт общий код и в какой момент «общее» превращается в свалку
- Границы модулей и пакетов как запрет, а не как пожелание
Сдержанность
Принципы, которые действительно стоит применять.
- KISS — самое простое решение, которое переживёт следующее изменение
- DRY — и куда более частая ошибка: применять его к вещам, которые лишь внешне похожи
- YAGNI — абстракция под будущее, которое так и не наступает
Уровни
| Уровень | Как это выглядит |
|---|---|
| Junior | Следует существующей структуре. Кладёт новый код туда, где уже лежит похожий. |
| Middle | Замечает, что у файла или фичи выросло две ответственности, и разделяет их. Спорит о направлении зависимостей с аргументами. |
| Senior | Задаёт структуру новой кодовой базы и может сказать, что она оптимизирует и чем жертвует. Знает, какие решения стоит отложить. |
Практика
Для начала
-
Назовите ответственность Возьмите файл длиннее трёхсот строк и напишите одно предложение о том, зачем он нужен. Если понадобилось «и», разделите файл.
-
Проследите зависимость Выберите фичу и нарисуйте, что она импортирует. Поищите то, что смотрит не в ту сторону.
-
Удалите абстракцию Найдите интерфейс ровно с одной реализацией и вставьте реализацию на место. Посмотрите, стало ли хоть где-нибудь хуже.
Дальше
-
Выделите модуль Возьмите фичу, переплетённую с остальным приложением, и дайте ей настоящую границу с одной точкой входа.
-
Разорвите цикл Найдите две части, которые импортируют друг друга, и уберите цикл без обходных путей.
-
Запишите правило Превратите договорённость, которую команда раз за разом повторяет на code review, в проверку, которую делает инструмент.
Проверьте себя
- Как отличить полезную абстракцию от слоя, который добавляет только лишнюю косвенность?
- Вы нашли дублирование — как решаете, объединять его или нет?
- По каким признакам видно, что файл делает слишком много, ещё до того как это станет очевидно?
- Какие решения в вашем текущем проекте будет дорого откатывать?
- Как бы вы объяснили структуру проекта человеку, который выходит завтра?
- Когда следование принципу делало ваш код хуже?
Ресурсы
- A Philosophy of Software Design — Джон Оустерхаут о сложности как о том, с чем на самом деле идёт борьба. Короткая книга с чёткой позицией, спорящая с несколькими вещами, которые все повторяют.
- Martin Fowler on software architecture — собрание эссе, в том числе о том, что вообще значит слово «архитектура» и почему за неё стоит платить. Начинать стоит с Who Needs an Architect?
- Refactoring — каталог того, как безопасно менять структуру. Ценность не столько в отдельных приёмах, сколько в привычке менять форму кода маленькими обратимыми шагами.
- Domain-Driven Design Reference — сжатая выжимка от самого Эрика Эванса, бесплатно. Полезна, даже если вы никогда не возьмёте DDD целиком: ограниченные контексты — самое внятное объяснение того, зачем вообще нужны границы.
- Guide to app architecture — взгляд самой команды Flutter. Его стоит прочитать именно потому, что он высказывается о конкретном стеке, а не о разработке вообще.