
Привет! Меня зовут Сергей Киселёв, я Head of Development Platform в MWS Cloud Platform. В 2023 году я пришёл собирать команду Development Platform (DevP) для разработчиков новой облачной платформы. Эта статья написана по следам моего доклада «Как с нуля построить Development Platform в отдельно взятой компании» на DevOops 2024. Далее расскажу о том, почему мы заботимся об общем коде, растим культуру разработки и почему только разработчик может сделать инфраструктуру для другого разработчика.
Что здесь происходит

Development Platform (DevP) — это инфраструктурная команда для Golang и Kotlin/Java-разработчиков. Мы адаптируем библиотеки и пишем свои, разрабатываем общие сервисы и помогаем устранять техдолг в продуктовых командах. Ещё отвечаем за деплой и использование логов, метрик и трейсов. Берём на себя роль интегратора инфраструктурных и продуктовых команд.

В других местах работы я всегда был разработчиком общих инфраструктурных решений. Приходилось делать много всего: строить каркас приложения, деплоить и мониторить, создавать фреймворк для выполнения асинхронных процессов и другие интересные штуки. Эти решения часто пользовались популярностью у моих коллег.
Что-то я делал неправильно, где-то подмечал, как другие делают ошибки. Почти всегда это лежало вне техники, а касалось подходов «к продаже» и внедрению общих решений. Помню, как написал фреймворк асинхронных задач, который встраивался с минимальными усилиями в любое наше java-приложение, и далее помог его внедрить без боли. В итоге я принял для себя основной подход к построению Development Platform — мы делаем решение разработчиками для разработчиков.
Стоимость общего кода

Хорошие подходы достойны, чтобы их распространять, не зря же вся индустрия пронизана концепцией copy-paste. Плохие подходы вредят и приводят к загниванию кодовой базы. Как правильно отделить первое от второго? В моей практике опытные разработчики всегда распространяют хорошие общие решения.
Обычно всё начинается с демонстрации своего работающего кода для обмена опытом в момент проведения код-ревью или при попытках понять «почему ничего не работает». Другие разработчики осознают ценность и копируют код к себе. После второго копирования разработчик начинает помышлять о выделении общего решения.
Тут кроется самая главная опасность: сделать никому не нужное общее решение — библиотеку. Особенно если разработчик нетерпелив и, не дождавшись пользователей, начинает писать общий код. Это может быть как «удобной» обёрткой над opensource-библиотекой, так и следование концепции NIH (Not Invented Here).
Но общий код не бесплатен — надо закладывать на него время, нервы и деньги. Наличие хорошей документации и читаемость способствуют переиспользованию. Ещё любое общее решение в компании конкурирует с миром opensource-решений и требует ресурсов для сопровождения. Поэтому стоит осознанно подходить к созданию общих инструментов.
Виды общего кода

В большинстве компаний есть опытные разработчики, которые влияют на остальных. Они могут быть из разных команд и, по сути, образовывать виртуальную команду Development Platform. В плохом сценарии они начинают войну за технологии и каждый тянет одеяло на себя. Это порождает зоопарк из библиотек и разных способов взаимодействия сервисов — API.
Для начала стоит легитимизировать команду Development Platform для решения общих проблем, чтобы самые опытные люди имели общие взгляды и могли договориться. У них должно появиться время на разработку общего кода и помощь с его внедрением.
Общий код можно классифицировать как:
Библиотека, решающая одну проблему. Например, у нас есть свой генератор OpenAPI для Golang/Kotlin и своя библиотека для компонентного тестирования микросервисов (по терминологии Фаулера). Тут важен принцип целесообразности: если есть хорошее opensource-решение, то не надо писать своё.
Код, описывающий «правильную» стратегию обработки данных. Сюда входят такие вещи, как построение каркаса приложения или стандартный процесс выполнения модификации объектов (с логами, метриками, трейсами и ретраями). Для Kotlin мы используем Spring Boot, а для Golang написали своё решение. Это всё следствие соглашений внутри команды и не должно использоваться снаружи.
Код, который используется одной командой разработки и/или слишком сложен для использования другими. Такие решения переродятся в общее только пройдя через очищающий рефакторинг с написанием тестов и документации. Но в текущем виде такой код нельзя переиспользовать, так как может приводить к проблемам сопровождения (техдолг).
Для нас было важно сделать явным работу над общим кодом и перестать его разрабатывать «партизанскими» методами. Общий код — способ говорить на одном языке с другими командами. Это полезно при ротации людей между командами и бутстрапе новых разработчиков. Именно поэтому мы создали отдельную команду для разработки и владения общими решениями.
Зачем нам договариваться

Нам удобно иметь Development Platform (DevP), которая отвечает за все общие решения и инструменты. Особенно если никто не хочет брать за них ответственность. Но этого мало, и важно сделать следующий шаг — организовать сообщество разработчиков в компании для обсуждения и совместной разработки общих решений.
Кто-то называет это inner-source культурой, но лично мне больше нравится называть это внутренним open-source. Вокруг общего кода выстраивается сообщество разработчиков и полезный опыт переиспользуется. Роль команды DevP — выстроить культуру обсуждения и совместной работы.
Мы у себя внутри пишем Design-документы, по спорным моментам прорабатываем варианты решений в ADR (Architecture Decision Record). Эти артефакты позволяют нам эффективно работать с другими разработчиками компании. И отвечать новым, почему мы делаем так, а не иначе.

В итоге упрощается внедрение общих решений. На начальном этапе мы прорабатываем решения до простоты и обсуждаем проблемы. Разработчики других команд могут принести общий код, и после этого они будут мотивированы внедрить это решение в свою команду. Благодаря этому подходу мы реализовали у себя слой библиотек для работы с API (пагинация и идемпотентность), и написано это было силами наших пользователей, а со стороны команды DevP было только согласование дизайна.
Как общаются сервисы и разработчики

Первая версия нашего облака содержит более 22 микросервисов. Они взаимодействуют между собой для создания пользовательских виртуальных машин, сетей или дисков. Контракты взаимодействия описывают люди разных команд. Если за этим не следить, это превратится в неконтролируемый рост различных видов API и способов их использования.

На раннем этапе строительства облака мы рассматривали разные варианты построения API и даже думали над архитектурой с общей шиной данных. Такой вариант сильно бы упростил нотификации и подписки на события сервисов. Но это единая точка отказа. А для внешних пользователей всё равно пришлось бы делать адаптер.
Мы планировали дать обычное http api для пользователей облака. Grpc был отброшен из-за его нераспространённости у пользователей. Сейчас мы используем OpenAPI как формат описания эндпоинтов наших сервисов. Наше публичное API — это подмножество приватного API, и мы эксперты в его потреблении. Это вынуждает нас делать API удобным. Многие новые фичи мы тестируем на своих внутренних решениях и пользователям отдаём уже более зрелое решение. Например, у нас одними из первых были реализованы фичи по пагинации в API.
У нас есть общий репозиторий всего приватного апи, и мы расширили OpenAPI-формат для более удобной кодогенерации (разметка видимости). Стандартные ogen и swagger нам не подошли по ряду причин (качество генерируемого кода, генерация документации), и это тянет на отдельную статью. Мы их выкинули через три месяца и перешли на своё решение. Текущее решение стало для нас способом говорить на одном языке.
Кто тестирует сервисы

У нас нет выделенной команды тестирования. Разработчики пишут тесты в процессе работы над кодом и для проверки работоспособности сервисов. Лично я люблю концепцию — тесты как строительные леса для работы над сервисами. И никто кроме разработчика не знает, как убедиться, что его код работает. Использование ручных тестировщиков очень дорого в наших сценариях разработки, и это замедляет релизные циклы.
Концепция уровней тестирования была вдохновлена презентацией Мартина Фаулера. В пирамиде тестирования есть: юнит-тесты кода, интеграционные тесты контрактов взаимодействия, компонентные тесты бизнес-логики, pre-deployment тесты для релизов. Юнит-тесты пишут многие разработчики, при этом мы у себя внутри используем mock-сущности для проверки сложных сценариев (кусочки бизнес-логики).
Интеграционные тесты — это чуть сложнее: они не используются для проверки бизнес-логики. Благодаря таким тестам можно убедиться, что нужные сервисы обеспечивают необходимый контракт взаимодействия. Например, можно проверить драйвер базы данных (R2DBC) с PostgreSQL, поднятым в отказоустойчивом сценарии (сценарий смены мастера). Благодаря этому мы уверены в надёжности взаимодействия базовых блоков любого сервиса.
Компонентные и pre-deployment тесты позволяют нам проверить совместимость микросервисов до релиза на прод. Оба типа тестов поднимают чужие сервисы локально или на CI после чего можно проверить, что ключевая бизнес-логика исходного сервиса работает. В компонентных тестах больше сценариев проверки, и разработчик пишет их в момент работы над кодом. Pre-deployment тесты — это «стоп-кран» для остановки релиза сервиса на прод: в момент релиза запускаются тесты всех команд относительно нового кода.

Как выкатывать и откатывать

Некоторые компании считают систему управления релизами ключевым компонентом Development Platform и часто выстраивают команду вокруг этой автоматизации. Мы идём от разработки и в первую очередь строим инфраструктуру, чтобы создавать надёжные сервисы. После этого мы приходим к единой системе по автоматизации выкладки сервисов в прод.
GIT — это уже стандарт индустрии, и сейчас все выбирают систему для удобной работы поверх хранения исходного кода. Мы используем Gitlab и запускаем задачи на нашем железном k8s-кластере. Внутри используем DIND для компонентных и pre-deployment тестов. Деплой тоже идёт из k8s-кластера в продовый k8s-кластер, развёрнутый поверх железа.
При деплое на стенд мы используем стандартный пайплайн, запускающий тесты и проверяющий другие аспекты выкладки. Конфигурация берётся из отдельного git-репозитория для каждого стенда. Осознанно не используем gitlab-темплейты для описания пайплайнов. Мы генерируем такие пайплайны кодом из модели приложения. Это позволяет взять под контроль описание сборки и «незаметно» добавлять новые фичи в наши пайплайны.
Не забыли мы и про проверку сервиса после релиза — на каждом стенде мы запускаем End-to-End (E2E) тесты и тесты производительности. E2E запускаются с некоторой периодичностью и позволяют нам быть уверенными в работоспособности облака. Таких тестов у нас немного, их цель — проверить основные сценарии пользователя из документации. Тесты производительности запускаются по необходимости, но в планах есть автоматизация их запуска для контроля регресса.
Как работать с сообществом

Наша ключевая идея — создавать команду Development Platform разработчиками для разработчиков. Мы сами пользователи своего решения, и это позволяет делать его лучше. Но крайне важно наладить коммуникацию с разработчиками других команд и сделать коллег своими соратниками, не заставляя их использовать плохие решения.
Внутренняя документация помогает наладить первый контакт и облегчить вхождение новых членов команды на внутренние митапы и для демонстрации новых фич. Крайне важно уважать вклад каждой команды и принимать от них изменения. Мы живём по модели внутреннего opensource, и не только наша команда имеет право контрибьютить в Development Platform. Построение сообщества — ключевая роль нашей команды.
Важно иметь поддержку наверху и налаживать горизонтальные связи между командами. Например, мы приносим MR с устранением техдолга в другие команды, предварительно договорившись об этом. Разработчики разных команд работают напрямую, минуя эскалацию в руководителей. Это позволяет нам не тормозить на планировании и бюрократии. Но крупные изменения всегда должны быть согласованы, нельзя ломать продуктовые планы из-за закрытия техдолга.

Что в итоге
На этом закончу обзор концепции построения нашей Development Platform и подсвечу ключевые идеи:
Общий код стоит дорого для бизнеса, и нельзя развивать его без осознанности.
Внутренние библиотеки всегда конкурируют с внешним миром, и надо осознавать мотивацию их написания.
Важно договариваться об инженерных решениях внутри компании и показывать договорённости новым членам команды.
Нужен единый язык общения разработчиков и сервисов — стандарт описания API.
Разработчик должен проверять свой код в момент написания, выкладки и эксплуатации.
Управление сообществом внутри — это важная работа для улучшения коммуникации внутри большой команды.
Только разработчик может сделать инфраструктуру для другого разработчика, потому что понимает его проблемы.
Комментарии (6)
deadlynch
24.07.2025 12:27Спасибо за статью.
Пару вопросов:
Как на практике происходит распространение решений? Канал с обновлениями, синки с лидами команд, knowledge sharing сессии и ТД
Является DevP команда неким "архитектором" платформы? Ваши ADR проходят ревью или другие процессы?
intr13 Автор
24.07.2025 12:27Как на практике происходит распространение решений? Канал с обновлениями, синки с лидами команд, knowledge sharing сессии и ТД
Каналы с информацией о релизах (дублирование ченджлога)
Публичные обсуждения архитектуры, например в прошлом году обсуждали про сервис меш
Синки с представителями (техлидами) команд по языкам
Рабочие группы по внедрению общих решений, без привлечения менеджеров, только разработчики
Приносим MR с кодом в продуктовые команды (своими силами пишем код для других команд)
Чаты поддержки, документация и дежурство в рабочее время
Недельные демо с обсуждением (записываем на видео)
deadlynch
24.07.2025 12:27Спасибо за ответ.
Ещё вопрос, но наверное больше к вашей практике. На проекте Golang прошел все стадии адаптации и принят в тех. радар. Около 3-5 команд запустили микросервисы на нём. Сейчас я хочу вынести общий инфра код в библиотеки(логгирование, работа с mq и тд). Естественно сейчас зоопарк, у кого ZeroLog, у кого zap и тд. Такая же история с кодом работы с очередями.
Подскажите, как бы вы стали действовать с такими вводными?
intr13 Автор
24.07.2025 12:27Поисследовать рынок опенсорса и код коллег, решить что надо писать, а чтоиуже есть готовое
Понять куда эта библиотека будет внедряться и набросать примерный план
Найти тех чьми силами будет делаться внедрение, собрать их вместе и обсудить планы и дизайн
Договориться с руководством о выделении времени команд, показать в чем ценность
Сделать базовый каркас для удобного веедрения библиотек И минимальный mvp библилюотеки (логи-метрики-трейсф отличная идея, либо взять работу с mq как альтернатива), главное не распылять силы
Внедрить мвп и показать итоги руководству, для этог собрать цифры профита, получить апрув и пилить дальше
intr13 Автор
24.07.2025 12:27Является DevP команда неким "архитектором" платформы? Ваши ADR проходят ревью или другие процессы?
Мы являемся фасилитаторами процесса дизайн ревью и общей архитектуры. В большинстве случаев мы "архитекторы" платформы. Но это не закрытое сообщество, всегда можно принести свое общее решение.
Бывает что продуктовые команды игнорируют общее платформенное решение. Для этого надо иметь мотивацию и продать этот выбор нашему CTO. Например так происходит, когда в прод нужно вчера. А общее решене еще не готово. Но со временем мы затаскиваем их на общие инструменты.
intr13 Автор
Если вам интересно узнать подробности про одну из тем, то напишите комментарий-вопрос. А я/мы расскажем это в формате ответа или полноценного рассказа.