Компания Gusto в последние годы активно развивает и совершенствует собственную систему модульности, которая играет ключевую роль в оптимизации архитектуры их основного продукта — платформы для управления зарплатами, льготами и HR. К 2024 году путь эволюции инструментов модульности Gusto прошёл несколько явных этапов, каждый из которых внёс значимые улучшения, позволяющие поддерживать структуру кода в понятной и управляемой форме, несмотря на масштабный рост продуктовой линейки и сложности инженерных задач. История развития инструментов модульности в Gusto тесно связана с применением open-source решения packwerk, изначально созданного Shopify, но к текущему периоду компания перешла на более производительный и функциональный аналог pks, написанный на Rust, что значительно повышает скорость и качество анализа кода. На начальном этапе «Адаптации» Gusto быстро и практически полностью перевела крупную часть кода в систему пакетов, число которых достигло почти двухсот. Это позволило отделить основные домены и компоненты, но в то же время выявило ряд проблем, связанных с нарушениями инкапсуляции и ограничений на зависимости.
Следующий этап «Расширения» сопровождался значительным ростом числа пакетов — их число более чем удвоилось, превысив четыре сотни, что дало возможность инженерным командам более детально сегментировать зоны ответственности. Но рост количества деталей стал затруднять восприятие и усложнял понимание общей структуры системы. Текущий этап «Доработки» направлен на балансировку между слишком мелкой разбивкой на пакеты и необходимостью сохранять понятную, устойчивую и логичную архитектуру. Ключевой инсайт, который сформировал современный подход — это необходимость четко отделить приложения от библиотек и пересмотреть логику взаимодействия между ними. Выяснилось, что большое количество пакетов на самом деле можно условно сгруппировать примерно в двадцать основных приложений или продуктовых сервисов, что значительно упрощает модель взаимодействия и соответствует бизнес-реалиям компании.
Однако прямая консолидация четырёхсот пакетов в двадцать больших приложений была невозможна из-за предпочтений команд продолжать внутреннюю детализацию и модульность в пределах каждой предметной области. Решение заключалось в создании нескольких логических слоев, которые структурируют пакеты и определяют правила их взаимодействия. Внешне архитектура выглядит как несколько вложенных уровней: внешняя оболочка приложения (application harness), слой инструментов разработки, базовые классы Rails и утилиты, которые служат общим фундаментом для внутренних продуктовых сервисов. Каждый нижний слой не должен знать о внутреннем устройстве верхних, поддерживая таким образом четкую направленность зависимостей и снижая риск циклических связей и избыточной связанности. Внутренний слой среднего уровня занимает порядка 80% кода и пакетов и именно здесь располагаются продуктовые сервисы, каждый из которых представляет собой обособленное подразделение функциональности.
Для того чтобы сохранить гибкость и детализацию внутри каждого продуктового сервиса, в структуре появилась возможность вложенного размещения пакетов. Это означает, что внутри одной и той же продуктовой области можно создавать свои пакеты и управлять ими независимо, что способствует более чистой и понятной внутренней модульности. Одним из сложных и ключевых вопросов стала организация API. Различия между API приложений и библиотек оказались существенными: приложения требуют более жестких ограничений и контрактов, что обеспечивает согласованность и стабильность внешних интерфейсов, в то время как внутренние пакеты функционируют скорее как библиотеки с более свободными контрактами. В результате Gusto ввёл концепцию выделенного пакета "_api" внутри каждого продуктового сервиса, который служит единственной точкой входа и взаимодействия с этим сервисом снаружи.
Это разграничение позволяет применять строгую политику приватности на уровне папок — внутренние пакеты видны и могут взаимодействовать между собой в рамках одного сервисного домена, но для внешних обращений разрешён только интерфейс из пакета "_api". Применение инструментов контроля приватности и зависимостей осуществляется с помощью расширений к инструменту pks, что обеспечивает автоматический контроль границ и уменьшает шум в виде ложных срабатываний. Особое внимание уделяется практике «нет ActiveRecord на границе», которая со временем показала неоднозначные результаты. Для внешних интерфейсов это строгое правило оправдано: объекты ActiveRecord не должны пересекать границы приложений, чтобы избежать связанных с сериализацией и контекстом проблем. Но внутри продукта, при организации коммуникации между внутренними пакетами в одном сервисе, такое ограничение излишне усложняло архитектуру и ухудшало производительность из-за дублирования слоев абстракции.
В частности, для реализации GraphQL-резолверов внутри домена отказ от прямого использования функционала ActiveRecord приводил к введению дополнительного паттерна репозитория, который увеличивал сложность взаимодействия и снижал эффективность загрузки и агрегации данных. Современный подход в Gusto допускает использование возможностей ActiveRecord внутри продуктового сервиса, что снижает число дополнительных уровней и улучшает общую производительность. Переход от packwerk к pks стал важным технологическим шагом. Новое решение, написанное на Rust, обеспечивает более высокую скорость анализа, возможность охватывать большее количество констант и не зависит от загрузчика Zeitwerk, что позволило расширить спектр обнаруживаемых проблем и упростить интеграцию инструментов проверки в процессы CI/CD. Таким образом, сегодняшняя архитектура и инструментарий Gusto — это результат длительной и осознанной эволюции модульности, направленной на гармоничное сочетание требований бизнеса и удобства разработки.
Структура из двадцати продуктовых сервисов с вложенными пакетами предоставляет инженерным командам необходимую свободу для реализации сложной функциональности и эффективного сотрудничества, одновременно обеспечивая понимание общей логики и системных границ. Благодаря такой организации кода становится проще создавать и развивать внутренние API, которые могут стать основой для новых продуктов и интеграций в экосистеме Gusto. Это способствует более быстрой адаптации к изменениям рынка и улучшению пользовательского опыта. В заключение стоит отметить, что опыт Gusto представляет собой интересный пример того, как крупная технологическая компания может успешно управлять сложностью монолитного приложения, последовательно выстраивая модульность и вырабатывая инструменты для поддержки долгосрочной стабильности и масштабируемости. Сообщество разработчиков Ruby и Rails активно обсуждает подобные подходы, и Gusto вносит заметный вклад в развитие практик модульности в рамках языка.
Для тех, кто интересуется глубже, доступны дополнительные материалы от инженеров Gusto, включая блоговые публикации и книги, посвящённые постепенной модульности, а также специализированные дискуссии на профессиональных Slack-серверах. Всё это подчеркивает значимость и перспективность выбранного ими пути в сфере современной разработки программного обеспечения.