Проблемы инструментария в больших проектах


    Основной инструмент любого программиста — язык программирования. Когда начинался проект мы выбрали Swift. Решили идти в ногу со временем, старый, но так горячо любимый Objective-C остался не у дел. Однако у Swift есть небольшая проблема и особенно она становится заметной, когда проект начинает расти – это проблема времени сборки проекта. Для понимания проблемы и размеров проекта, попробуем сравнить среднее время сборки за неделю на всех проектах студии.


    image


    Как видно из графика, медиана времени сборки проекта ZenitOnline больше всех представленных в несколько раз. И поверьте, остальные проекты не такие уж и маленькие. Особенный интерес представляет Objective-C проект, который в свою очередь по размерам сопоставим с нашим проектом. Оба содержат 100+ экранов. За полтора года разработки мы успели достичь следующих цифр:


    • ≥ 2500 файлов;
    • ≥ 600 ресурсов;
    • ≥ 160 000 строк кода.

    Однако счастье длилось недолго и, примерно, на этапе 1500 файлов xcode решил, что нам больше не нужно собирать проект и просто отказался это делать, аргументируя свое поведение следующей ошибкой:


    Unable to spawn process (Argument list too long)

    В поисках ответа на SO, Swift Jira и Open Radar, я понял, что решить это в одно действие не получится. Исходя из появившейся ошибки, найти выход из этой ситуации можно несколькими способами:


    1. Уменьшить количество папок в проекте, тем самым скинуть все в одну кучу, чтобы путь до компилируемых файлов уменьшился. Это решение не зашло из-за любви к порядку в проекте.
    2. Переместить проект выше в иерархии папок в системе. Это спасло нас на какое-то время, но далее уменьшать путь до директории проекта стало невозможно.
    3. Последним решением, которое реально могло помочь нам на тот момент, стала модульная архитектура проекта. Собственно, этого способа решать проблемы мы придерживаемся и до сих пор.

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


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


    image


    3rd Party Dependency – самый нижний слой, отвечает за сторонние зависимости. Все эти сторонние библиотеки подключаются к проекту с помощью CocoaPods. Ничего необычного по сравнению с плоской архитектурой приложения.


    Core Frameworks – после слоя со сторонними библиотеками идет основной блок фреймворков, которые в дальнейшем импортируются во все части приложения и используются так или иначе.


    • Common – модуль, в котором содержатся основные константные значения. Например, локализация, все картинки, extensions и другие не завязанные на UI и сервисном слое классы и утилиты.
    • Services – этот модуль содержит в себе все необходимые классы для работы с сетью и данными. Здесь можно найти модели сервисного слоя, утилиты для работы с сетью, БД и другими данными в приложении.
    • Reusable – в этот фреймворк мы решили вынести все переиспользуемые UI-элементы нашего приложения: поля ввода, ячейки, адаптеры и другие вьюшки.
    • Analytics – исходя из названия, думаю, легко понять, что все классы аналитики находятся тут.
    • Core – фреймворк, в котором не находится ни строчки кода. Единственная его цель – это объединение импортов всех модулей, входящих в Core слой. Это необходимо для дальнейшей удобной линковки переиспользуемых фреймворков в других модулях.

    Feature Frameworks – это фреймворки, которые содержат в себе отдельно взятые фичи. Каждый фреймворк из этого слоя импортирует в себя Core уровень и другие зависимые от него фичи. Такой подход позволяет удобно изолировать работу и код разных разработчиков.


    Выводы


    Какую проблему решает модульность? Как минимум две проблемы были решены благодаря такому подходу:


    • Скорость сборки проекта.
    • Невозможность скомпилировать проект.

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


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


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


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

    Surf
    Компания

    Комментарии 1

      0
      скорее всего, про время сборки модульного проекта немного (скорее много) лукавите, когда в проект подключаются поды — xCode уже заметно увеличивает время сборки, но когда используются отдельные поды под фичи, которые включает дев поды и прочие поды — не только время сборки улетает в космос, но и простой autocomplete и jump to definition отказываются работать из-за слишком сложного дерева зависимостей.
      Ну и инкрементальная сборка только того, что изменили — утопия… которая не работает.

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

      Самое читаемое