Модуляризация – локализация изменений (PAI)

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

Как же можно добиться локализации и изоляции?

Базовый принцип, помогающий локализовать и изолировать код называется «Low Coupling/High Cohesion», Низкая связность/Высокая когезия (когезия – физический термин, который хорошо подходит для перевода названия данного принципа). Его суть в том, что тесные связи кода между собой должны быть локализованы в одном логически цельном куске кода (функции, классе, модуле, пакете), а связей с другими не связанными частями кода должно быть минимум и они должны быть максимально простые и понятные.

Еще Эд Йордон и Ларри Константайн в своей книге 1977 г.! «Structured Design» доказали, а Кент Бек в 2025 г. уже в своей книге «Чистый дизайн» повторил, что стоимость программного кода примерно равна связности (Coupling): чем выше связность (coupling), тем дороже будет стоимость программного кода. Данный принципе на уровне функционального программирования сводится к инкапсуляции кода в функциях, на объектно-ориентированном уровне – к инкапсуляции функций, данных и кода в классах, а на уровне проектирования всей системы – в модуляризации и разбивки кода по модулям/пакетам. Его более узкой формулировкой является принцип единой ответственности (Single Responsibility) из набора принципов SOLID.

Как модуляризация выглядела у нас?

В нашем случае идея преобразилась в следующую: мы выделяем отдельный «аквариум» (модуль/пакет) с кодом каждой продуктовой команде (а значит – и каждому отдельному разработчику этой команды). Все свои изменения разработчик локализует в своем пакете. Число внешних связей мы сокращаем и жестко контролируем. Таким образом, мы получаем минимально возможную связность (Coupling) нашего проекта, а значит, по доказанному (и выше упомянутому) примерному равенству и минимальную стоимость программного кода.

Для упрощения процесса внедрения модуляризации было принято решение разбить его (процесс) на 2 этапа: сначала разбивка на модули/пакеты, которые будут храниться в одном монорепозитории. А затем, по необходимости эти модули/пакеты будут выноситься в отдельные репозитории. Внедрять модуляризацию в наших условиях (и с таким принятым решением) было довольно просто, т.к.внедрение совпало со стремительным ростом нашей организации и числа продуктовых команд. В этих условиях мы просто поставили обязательное требование каждому новому iOS-разработчику: перед началом своей работы он доложен был завести отдельный модуль/пакет, в который он и должен был далее вносить все свои изменения. Возня с различными репозиториями была отложена и проблем нам не доставляла.

Как ранее упоминалось в публикации про нашу стартовую точку, весь имевшийся к тому моменту код писался всего 3-4 разработчиками на протяжении лет 4-5. Было его не так много, и весь он был довольно хорошего качества (нам повезло) – поэтому модуляризовывать старый код было не так сложно. Возможно, в этом нам также помог шаблон, называемый «удушение монолита».

Однако, мы не ставили перед собой цель модуляризации ради модуляризации – мы действовали по закону Парето. 20% усилий, дают 80% результата. Именно поэтому в нашей кодовой базе до сих пор есть большие связные куски кода, которые мы зовем «монолитом». Т.к. эти куски кода редко модифицируются, плохо покрыты тестами и не доставляют нам абсолютно никаких проблем, то и не нуждаются в рефакторинге, как мы упоминали в одной из прошлых публикации (ссылка).

Но какие же плюсы маленьких и дружных стартапов мы смогли получить у себе в огромном энтерпрайзе, о которых я упоминал в прошлом посте? А об этом мы поговорим в следующем посте :) Не переключайтесь!