В современном мире разработки программного обеспечения, где масштабируемость и поддерживаемость становятся ключевыми требованиями, компании все чаще обращаются к модульной архитектуре как к эффективному решению сложных инженерных задач. Gusto, ведущий разработчик в области управления зарплатами, льготами и HR-сервисами, демонстрирует пример того, как эволюционируют инструменты и подходы к модульности в крупных кодовых базах. В 2024 году Gusto активно переосмысливает и совершенствует свои инструменты модульности, чтобы обеспечить сбалансированное развитие как внутренней структуры проектов, так и их внешних API, что напрямую влияет на качество и скорость вывода новых продуктов на рынок. Ключевым фактором в этом процессе становится переход от работы с сотнями мелких пакетов к более осмысленному объединению модулей в продуктовые сервисы, что облегчает восприятие и управление кодовой базой для разработчиков, инженеров и команд в целом. В основе новой стратегии лежит понимание различий между приложениями и библиотеками и того, как их API должны отличаться в плане ограничений и использований.
Ранее Gusto использовал инструмент packwerk, вдохновленный опытом Shopify, который позволял делить код на пакеты и контролировать зависимости между ними. В первый этап внедрения инструментов модульности, начавшийся в 2020 году, были созданы первые пакеты, и за несколько месяцев их количество выросло почти до двухсот. Этот процесс помог выделить доменные границы и выявить места, в которых структура требовала улучшения. Однако появление большого количества пакетов поставило перед командой вызов – как справиться с растущей сложностью и сделать структуру более понятной и управляемой. Отвечая на этот вызов, Gusto вошел в фазу расширения, где число пакетов удвоилось, превысив четыреста.
Несмотря на технические недостатки и нарушения модульных границ в отдельных пакетах, этот шаг стал важным для визуализации архитектурных границ и определения направлений дальнейшего развития. В течение следующего этапа развития, фазы улучшения, была поставлена задача снять напряжение, связанное с обилием пакетов, которое мешало разработчикам быстро ориентироваться в системе. Центральный вопрос заключался в том, как снизить количество элементов до более управляемого числа приложений, соответствующих реальным продуктовым сервисам компании. Результатом анализа стало выделение около двадцати ключевых приложений – значительное упрощение по сравнению с сотнями пакетов. Однако полностью избавиться от пакетов не было возможным, поскольку команды ценили возможность структурирования внутренних компонентов в рамках своих доменов.
Для решения этой дилеммы Gusto разработал инновационный подход, который включает концепции слоев, продуктовых сервисов и вложенных пакетов. Слои отражают различия во взаимодействиях и областях ответственности в системе. Внешний слой — это application harness, непосредственно корень Rails-приложения, далее следует слой специализированных инструментов разработки, базовые классы Rails, а также набор утилит, используемых во многих сервисах. Продуктовые сервисы занимают центральную часть этой модели и реализуют основные бизнес-функции. Важно, что каждое нижележащее звено слоя не знает ничего о верхнем, что гарантирует минимальные зависимости и слабую связанность, критичные для масштабируемой архитектуры.
В целях повышения наглядности и контроля структура пакетов была реорганизована в папки, соответствующие слоям, с применением расширений для packwerk, позволяющих жестко контролировать доступ и зависимости между слоями. Таким образом, внутренняя часть кода, сосредоточенная в продуктовых сервисах, структурируется по вложенным пакетам, отражающим доменные границы и позволяющим поддерживать чистоту архитектуры. Этот подход создает пространство для масштабирования кода, сохраняя при этом ясность и экономя время команды на поиск и понимание контекста. Одним из наиболее сложных вопросов при построении такой модели стала организация API. Возникает главный вопрос – что считать API внутри продуктовых сервисов и как отделить внешние интерфейсы от внутренних технических деталей? Разработчики Gusto признали, что в контексте одного продуктового сервиса пакеты ведут себя скорее как библиотеки с меньшими ограничениями, чем полноценные приложения с определенными API-границами.
Следствием этого стало введение особого внутреннего порядка: создание специального пакета «_api» внутри каждого сервисного пакета, который отвечает за внешний интерфейс и служит точкой взаимодействия с другими приложениями. Для остальных внутренних пакетов в рамках сервисов была внедрена политика приватности и контроля доступа на уровне папок, что помогает избежать нежелательных пересечений и позволяет командам самостоятельно управлять внутренними связями. Такой разделительный подход важен для сохранения баланса между свободой разработки внутри команд и необходимыми ограничениями при взаимодействии различных продуктовых сервисов. Особое внимание было уделено роли ActiveRecord на границах сервисов. Традиционно в Rails-приложениях советовали избегать передачи ActiveRecord-объектов через API, чтобы не создавать излишней связанности и обеспечить чистоту слоев.
Однако Gusto выявил, что внутри одного продуктового сервиса такой запрет может привести к усложнению и понижению производительности из-за необходимости лишних преобразований и дополнительных уровней абстракции. Примером служит архитектура GraphQL-схемы, где прямое использование ActiveRecord объектов упрощает работу с данными и позволяет избегать распространенных ошибок вроде n+1 запросов, улучшая эффективность и скорость отклика. Именно поэтому в рамках отдельных пакетов внутри сервисов политика использования ActiveRecord может быть более гибкой, а жесткие ограничения применяются только к границам продуктовых сервисов. Эта идея заключена в концепции разделения API, где внешний интерфейс строго контролируется, а внутренние детали остаются открытыми и адаптируемыми к конкретным нуждам разработки. В техническом плане Gusto переходит от традиционного инструмента packwerk к более современному и быстрому решению – pks, написанному на Rust.
Этот инструмент обеспечивает более глубокий и точный анализ кода, способен эффективно работать со всеми Ruby-константами и дает инженерам обратную связь в режиме реального времени. Быстродействие и расширенные возможности pks способствуют более интенсивному внедрению политики модульности и позволяют командам быстрее реагировать на нарушения архитектурных правил и улучшать структуру кода. Важным аспектом стратегии является не только техническая реализация, но и культурное изменение внутри инженерного коллектива. Gusto приглашает разработчиков делиться опытом, обсуждать сложные вопросы модульности на специализированном Slack-сервере и активно обучает команду новым методам построения модульных и масштабируемых сервисов на базе Ruby on Rails. Такая вовлеченность усиливает общее понимание и согласованность действий, что позитивно сказывается на развитии продукта и ускоряет появление новых функций, востребованных клиентами.
Итогом всех этих изменений в 2024 году стала выверенная, гибкая и позволяющая динамично развиваться архитектура, в центре которой находятся продуктовые сервисы с четко определенными внешними API и продуманной внутренней структурой. Этот подход помогает разворачивать новые интеграции, улучшать коммуникацию между командами и строить качественный, надежный и поддерживаемый код. Модульность перестала восприниматься как абстрактная техническая задача и превратилась в мощный инструмент для достижения стратегических целей бизнеса. Gusto продолжает развивать свои инструменты и практики, делая упор на баланс между разделением ответственности и удобством работы разработчиков. Это создает прочную основу для дальнейших инноваций и масштабирования, позволяя компании уверенно отвечать на вызовы рынка и соответствовать ожиданиям клиентов.
В конечном итоге, опыт Gusto является ценным примером для других организаций, которые стремятся оптимизировать свои процессы разработки и построить устойчивую архитектуру, готовую к переменам и росту в быстро меняющемся технологическом мире.