- PVSM.RU - https://www.pvsm.ru -

Я усвоил всё это, когда работал над новаторскими проектами в начинающих стартапах, где в качестве программной архитектуры выбирались сомнительные решения, которые вели к недоделанным сервисам и хрупким, перемудрёным локальным конфигурациям, а также подрывали дух разработчиков, которые не тянули излишнюю сложность.
Ну и прежде чем описывать подводные камни такого решения, уточню, на что конкретно вы подписываетесь, когда слишком рано переходите на микросервисы:
| Слабое место | Как проявляется | Последствия для разработчика |
| Сложность развёртывания | Необходимость оркестрировать более 5 сервисов для работы одной функции | Лишние часы работы над каждым релизом |
| Хрупкость локальной разработки | Расползание Docker, сломанные скрипты, платформо-зависимость | Медленный онбординг, частые сбои |
| Дубликация CI/CD | Несколько пайплайнов с повторяющейся логикой | Дополнительная плата за каждый сервис |
| Связывание сервисов | «Разделённые» сервисы, тесно связанные общим состоянием | Медленное внесение изменений, дополнительные хлопоты по координации |
| Издержки наблюдаемости | Распределённая трассировка, логирование, мониторинг | Требуются недели для должной настройки всех инструментов |
| Фрагментация наборов тестов | Тесты разбросаны по сервисам | Ненадёжные тесты, низкая уверенность |
Далее я расскажу, почему микросервисы на ранних этапах зачастую дают обратный эффект, когда они действительно помогают, и как структурировать системы стартапа для повышения скорости его роста и выживаемости.

Если вы создаёте продукт SaaS, даже простая обёртка базы данных SQL в конечном итоге может привнести много внутренней сложности, связанной с её бизнес-логикой. Вы также можете прибегать к различным интеграциям и фоновым задачам, которые позволяют преобразовывать один набор данных в другой.
Со временем ваше приложение может обрасти ненужными функциями и стать беспорядочным. Монолиты же отличаются тем, что продолжают работать — даже в беспорядочном виде они позволяют команде акцентировать внимание на самом важном:
Самое большое преимущество монолитов в простоте их развёртывания. Как правило, подобные проекты создаются на базе существующих фреймворков — это может быть Django для Python, ASP.Net для C#, Nest.js для Node.js и так далее. Придерживаясь монолитной архитектуры, вы получаете значительное преимущество перед вычурными микросервисами — обширную поддержку со стороны опенсорсного сообщества и мейнтейнеров проекта, которые изначально создавали эти фреймворки для обеспечения работы в виде единого процесса, монолитного приложения.
Расскажу одну историю. Как-то в стартапе, занимавшемся недвижимостью, я вёл команду фронтенда и периодически консультировал ребят из бэкенда в выборе технологий. Так вот, наше приложение создавалось на Laravel и претерпело интересную эволюцию. Начиналось оно с небольшой информационной таблицы для риелторов, но в итоге разрослось в куда более масштабную систему.
Со временем приложение превратилось в многофункциональный программный пакет, который обрабатывал сотни гигабайт документов и интегрировался с десятками сторонних сервисов. При этом он всё также опирался на откровенно простой стек PHP, функционирующий на сервере Apache.
Наша команда активно налегала на лучшие практики, рекомендованные сообществом Laravel. И это себя оправдало. Мы смогли значительно масштабировать возможности приложения, продолжая соответствовать задачам и ожиданиям бизнеса.
Интересно, что нам ни разу не пришлось разделять систему на микросервисы или применять более сложные паттерны инфраструктуры. Таким образом мы избежали большого объёма непредвиденной сложности. Простота архитектуры дала нам важный рычаг для решения задач. И это отражает опыт других людей. К примеру, в статье компании Basecamp «Majestic Monolith [1]» авторы расписывают, почему на начальных этапах простота подобна суперсиле.
Люди нередко сетуют, что сложно делать монолиты масштабируемыми, но подобные сложности могут быть вызваны неудачной модульной структурой ПО.
Вывод: грамотно структурированный монолит позволяет команде сосредоточиться на поставке продукта, а не тушении «пожаров».
Многие инженеры сразу начинают с микросервисов, считая, что «это правильный путь». Да, при больших масштабах они помогают. Но в стартапе аналогичная сложность ведёт к торможению разработки. Микросервисы оправдывают себя, только когда у вас есть реальные узкие места, препятствующие масштабированию, большие команды или независимо развивающиеся направления.
А до этого? До этого вы платите всю ту же цену, но преимуществ не получаете. То есть та же дублирующаяся инфраструктура, хрупкие локальные конфиги и медленное внесение доработок. Например, компания Segment именно по этой причине в конечном итоге отыграла назад разделение на микросервисы [2] — слишком много затрат при недостаточной ценности.
Вывод: микросервисы — это инструмент для масштабирования, а не стартовый шаблон.
В одной из начинающих команд, которую я консультировал, решение разделить сервисы создало больше хлопот по координации разных отделов, нежели принесло технической пользы.
Архитектура определяла не только форму кода, но также схему планирования, оценки и поставки продукта. Причём эти организационные издержки на первых порах легко упустить из виду, а потом становится слишком поздно.

Диаграмма: издержки координации растут линейно с количеством сервисов и экспоненциально при добавлении продакт-менеджеров, дедлайнов и несогласованных таймлайнов
Вот несколько наиболее распространённых анти-паттернов, которые могут просочиться в систему на ранних этапах.

В теории мы часто видим предложения разделять ПО на основе бизнес-логики — пользовательский сервис, сервис продукции, сервис заказов и так далее. Такой подход часто заимствуется из принципов предметно-ориентированного дизайна (Domain-Driven Design, DDD) или чистой архитектуры (Clean Architecture), которые имеют смысл в больших масштабах. В случае же начинающих продуктов, ещё не достигших стабильности и надёжности, они могут привести к преждевременному окостенению структуры.
В итоге вы получаете:
Ещё в одном проекте я наблюдал, как команда разделила пользователя, аутентификацию и авторизацию в отдельные сервисы. Это усложнило развёртывание и затруднило координацию сервисов при любой операции API, какую бы разработчики ни создавали.
В реальности бизнес-логика не отображается непосредственно в границы сервиса. Преждевременное разделение может сделать систему более хрупкой и часто замедляет внесение изменений.
Вместо этого нужно точечно изолировать узкие места, опираясь на реальный план масштабирования, а не теоретическую элегантность.
Когда я обучал начинающие команды, мы иногда использовали для симуляции будущего разделения сервисов внутренние переключатели деплоев — не создавая операционных издержек. Это позволяло продакт-менеджерам и инженерам плавно исследовать границы возможностей, прежде чем фиксировать недостаточно зрелую инфраструктуру.
Вывод: разделяйте систему не по теории, а по фактическим узким местам.
При разработке приложения обычно важно следующее:
При работе с микросервисами всё это нужно умножать на количество сервисов. Если ваш проект имеет структуру монолита, вы можете упростить себе жизнь, наладив централизованную конфигурацию CI/CD (при работе с GitHub Actions или GitLab CI). Некоторые команды разделяют микросервисы по разным репозиториям, что сильно усложняет сохранение согласованности кода и одинаковых конфигураций без привлечения дополнительных инструментов, а значит и усилий.

Для команды из трёх человек это перебор. Переключение между репозиториями и дополнительные инструменты увеличивают время разработки каждой функции.
Исправить эту проблему можно по-разному. В молодых проектах самое важное — это хранить весь код в едином репозитории. Такой подход гарантирует присутствие в продакшене одной версии программы, а также упростит координацию код-ревью и взаимодействие внутри небольших команд.
В случае проектов Node.js я настоятельно рекомендую использовать инструмент nx или turborepo, так как они:
Эти инструменты экономят время, которое в противном случае тратится на связующий код или очередное продумывание оркестрации. Но есть у них и существенные недостатки:
Итак: инструменты вроде nx или turborepo в небольших командах повышают скорость работы с единым репозиторием — если только вы готовы вкладываться в поддержку их чистоты.
При разработке микросервисов на базе Go на ранних стадиях будет удобно трудиться в едином пространстве, используя директиву replace в go.mod. В конечном итоге по мере расширения ПО можно будет без лишних усилий выделить модули Go в отдельные репозитории.
Вывод: единый репозиторий с общей инфраструктурой экономит ваше время, а также обеспечивает согласованность и душевный покой.
Если для локального запуска вашего приложения требуется три часа, кастомный скрипт оболочки и марафон из контейнеров Docker, значит вы утратили скорость.
Молодые проекты часто страдают от:
Из своего опыта могу сказать, что проекты, которые мне доставались от других команд, часто создавались под одну операционную систему. Некоторые разработчики предпочитали писать под macOS и никогда не озадачивались поддержкой своих скриптов оболочки под Windows. В командах, с которыми я сотрудничал, бывали инженеры, работавшие на машинах с Windows. И им часто приходилось переписывать скрипты оболочки или капитально перестраивать процесс налаживания работы локальной среды.
Со временем мы стандартизировали настройку среды для ОС разработчиков, чтобы уменьшить трения внутри команды. Это стало небольшим вложением, которое позволило сэкономить немало часов работы с приходом каждого нового инженера. Было нелегко, но мы осознали всю важность того, чтобы код запускался на ноутбуке любого нового разработчика, какую бы систему тот ни использовал.
Приведу пример ещё одного проекта, где инженер создал хрупкую конфигурацию микросервиса, который поток обработки контейнеров Docker смонтировал в локальную файловую систему. Естественно, когда ваш ПК работает под Linux, вы платите очень мало за выполнение процессов в виде контейнеров.
Но вот приход в команду нового фронтенд-разработчика, у которого на рабочем ноутбуке стояла Windows, привёл к кошмару. Ему приходилось запускать аж десять контейнеров, чтобы просто увидеть свой UI. При этом всё постоянно ломалось — то тома, то сеть, то проблемы с совместимостью контейнеров — и сама конфигурация была очень плохо задокументирована. Всё это создало немало трений в процессе онбординга.
В итоге мы вместе придумали прокси на Node.js, который имитировал конфигурацию nginx/Docker без контейнеров. Да, элегантности этому решению не хватало, зато оно позволило новому разработчику, наконец, включиться в общую работу.

Вывод: если ваше приложение работает только в одной ОС, то от провала продуктивности вас отделяет лишь один ноутбук.
Совет: в идеале старайтесь делать
git clone <repo> && make up, чтобы проект запускался локально. Если это невозможно, необходимо вести актуальный файл README с инструкциями для Windows/macOS/Linux. Сегодня существуют языки программирования и наборы инструментов, которые плохо работают под Windows (например, OCaml), но популярный программный стек прекрасно себя чувствует в любой из распространённых операционных систем. Ограничение конфигурации лишь одной ОС зачастую говорит о недостаточном продумывании процесса разработки.
Помимо архитектуры, стек технологий также определяет степень болезненности микросервисов — не каждый язык хорошо дружит с этой архитектурой.
Очень важно выбрать подходящий технологический стек с самого начала. Если вы ориентированы на быстродействие, обратите внимание на JVM с её экосистемой и возможностью масштабного развёртывания и выполнения артефактов в архитектурах на базе микросервисов. Если же вас интересует частое внесение доработок и оперативное прототипирование без масштабирования инфраструктуры развёртывания, то вполне подойдёт что-то вроде Python.
Довольно часто команды осознают, что выбрали совсем неподходящую технологию, которая изначально такой не казалась. В итоге им приходится вкладываться в переписывание бэкенда на другом языке (как в этом случае [3], когда инженерам пришлось выкручиваться из ситуации со старой кодовой базой на Python 2 и в итоге переносить её на Go).
С другой же стороны, если вам действительно это важно, можете соединить несколько языков программирования с помощью таких протоколов, как gRPC или асинхронного обмена сообщениями. И часто делают именно так.
Иногда вы подходите к точке, когда нужно расширить функциональность с привлечением машинного обучения или ETL-инструментов. В таком случае вы просто отдельно создаёте МО-инфраструктуру на Python, поскольку он имеет богатую экосистему предметно-ориентированных библиотек, которой не может похвастаться ни один другой язык. Но такие решения нужно принимать, когда в штате есть достаточно специалистов. В противном случае небольшая команда погрузится в пучину сложности совмещения нескольких программных стеков.
Вывод: сопоставляйте используемые технологии со своими возможностями, а не амбициями.
Микросервисы несут с собой незримый набор потребностей:
В монолите баг может раскрываться простой трассировкой стека. В распределённой же системе это подразумевает вопрос «Почему сервис А падает, когда развёртывание сервиса В отстаёт от развёртывания С на 30 секунд?» Здесь вам придётся прилично вложиться в набор технологий для наблюдаемости. Чтобы сделать всё «правильно», потребуется определённым образом снарядить ваше приложение. Это может подразумевать интеграцию OpenTelemetry для поддержки трассировки или привлечение инструментов облачного провайдера вроде AWS XRay, если вы реализуете крупную бессерверную систему. Но в этот момент вам придётся полностью сместить акцент с кода приложения на создание сложной инфраструктуры мониторинга, которая обеспечит уверенность в должном функционировании системы в продакшене.
Естественно, какие-то механизмы наблюдаемости нужно реализовывать и в монолитах, но в них всё это намного проще, нежели согласование подобных действий во множестве сервисов.
Совет: уясните, что распределённые системы требуют вложений, поэтому их использование влечёт за собой целый класс новых инженерных задач.
Несмотря на упомянутые сложности, в некоторых случаях разделение на уровне сервисов оказывается очень кстати. Это может быть:
Крупные компании сталкивались с аналогичными проблемами. К примеру, команда разработки в Uber описала свой переход [4] к предметно-ориентированной архитектуре микросервисов — и не из-за её теоретической чистоты, а в результате возникновения реальной сложности взаимодействия между командами и достижения пределов масштабирования. Их статья является хорошим примером того, как микросервисы могут работать, когда у вас достаточно зрелая организация, и есть достаточный операционный ресурс для их поддержки.
В одном проекте, который тоже касался работы с недвижимостью, нам от предыдущей команды достался код Python, который выполнял аналитику, загружая полученные данные в БД MS-SQL. В той ситуации мы поняли, что будет излишне затратно и бессмысленно создавать поверх такого механизма приложение на Django. В имевшемся коде использовались другие зависимости среды выполнения, и он был достаточно изолирован, поэтому мы держали его отдельно и возвращались к нему, только когда что-то шло не так. Такой подход сработал даже для нашей небольшой команды, поскольку этот сервис аналитики редко требовал изменений или обслуживания.
Вывод: используйте микросервисы при наличии расходящихся в своей сути рабочих процессов, а не просто, потому что они сулят чистоту.
Если вы готовите свой первый продукт, вот вам несколько рекомендаций:
make up. Если этого недостаточно, тщательно продумывайте все шаги, записывайте видео с экрана и добавляйте скриншоты. Если ваш код будет выполнять начинающий разработчик, он наверняка столкнётся с проблемой, и вам придётся объяснять, как её решить. Я на личном опыте понял, что документирование всех возможных проблем в каждой операционной системе избавляет от необходимости тратить время на объяснение, почему определённые моменты в локальном сеттинге не работают.scp) на сервер вручную, этот процесс можно автоматизировать с помощью систем контроля версий и CI/CD. Когда конфигурация должным образом автоматизирована, вы просто забываете об инфраструктуре непрерывной интеграции и сосредотачиваетесь на функциональности. Я видел немало команд и основателей компаний, которые при работе со сторонними подрядчиками зачастую экономят на CI/CD, что приводит к негодованию разработчиков, которым надоедает деплоить всё вручную.Ну и самое главное: оптимизируйте систему под скорость разработки.
Скорость подобна кислороду для вашего стартапа. Преждевременное внедрение архитектуры микросервисов приводит к постепенной утечке этого кислорода, пока в конечном итоге стартап просто не задохнётся.
Вывод: начинайте с простого, придерживайтесь прагматичности и используйте разделение только при необходимости.
Я сталкивался с проектами, которые переходили на микросервисы раньше, чем следовало бы, и вот несколько рекомендаций на подобный случай:
docker-compose.yml, которые бы позволили обычному разработчику легко запускать программу одной командой.
Подытожим. Если вы всё равно собираетесь переходить на микросервисы, то должны заранее учитывать сопутствующие издержки в плане дополнительных усилий по развёртыванию и обслуживанию, необходимые для создания конфигурации, с которой сможет работать любой инженер в вашей команде.
Вывод: если вы решили перейти на сложную архитектуру, приложите усилия, чтобы сделать её управляемой.
Преждевременный переход на микросервисы приведёт к непосильным издержкам. Придерживайтесь простоты — она позволит вам оставаться на плаву — и прибегайте к разделению, только когда это уже явно необходимо.
Первым делом — выживание. Масштабирование потом. Используйте простейшую систему, которая работает, и оправдывайте каждый привносимый в неё уровень сложности.
Автор: Bright_Translate
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/420138
Ссылки в тексте:
[1] Majestic Monolith: https://signalvnoise.com/svn3/the-majestic-monolith/
[2] отыграла назад разделение на микросервисы: https://segment.com/blog/goodbye-microservices/
[3] этом случае: https://blog.khanacademy.org/go-services-one-goliath-project/?utm_source=blog.quastor.org&utm_medium=referral&utm_campaign=khan-academy-s-migration-from-python-to-go
[4] описала свой переход: https://www.uber.com/en-HR/blog/microservice-architecture/
[5] Источник: https://habr.com/ru/companies/ruvds/articles/909548/?utm_source=habrahabr&utm_medium=rss&utm_campaign=909548
Нажмите здесь для печати.