Близится завершение большого этапа реализации первого продукта, где, с некоторого момента времени, я являюсь техническим владельцем. И чем лучше работает продукт, тем больше появляется времени на проведение ретроспективы по всему полученному за это время опыту. Самое неожиданное открытие, которое вроде как не открытие, но об этом почему-то не говорят так много, как должно было бы говорить, заключается в том, что используемые в проекте технологии влияют на успешность проекта. Даже не так. Используемые технологии на разных уровнях определяют качество продукта и скорость его создания. И происходит это не только на уровне исполнения кода, протоколов передачи данных, операционных систем, но и на уровне команды реализующей продукт. И ещё большим открытие стало после осознания, что при разработке нового продукта - очень редко происходит обсуждение не только конкретных фреймворков, языков и паттернов с технической точки зрения, но и влияние всего этого на организационные и коммуникационные аспекты работы команды. Самым сильным примером этого открытия, про который я и хочу рассказать в этой статье, является сравнение архитектурного подхода к реализации продукта.
Представленный ниже список является субъективным набором наблюдений о преимуществах работы в команде проекта на микросервисной архитектуре по сравнению с работой в команде проекта с монолитным подходом. Список сформирован на основании опыта работы в самых разных командах и продуктах. Монолиты, распределённые монолиты, микросервисы. В релизных и продуктовых командах, работающих “по водопаду” и с “гибкими методологиями”. В больших проектах с несколькими параллельно работающими командами, и в команде реализующей end-to-end продукт. Будучи младшим разработчиком, равноправным участником команды или тех. лидом.
“Преимущества” в статье - это набор факторов, показывающий как микросервисы помогают реализовывать некоторый абстрактный продукт с большей скорость и качеством по сравнению с аналогичной реализацией в монолите. Безусловно, существуют продукты с огромными бюджетами, командами состоящих только из профессионалом с десятками лет опыта, с устраивающим всех полугодовым time-to-market и прочими факторами при которых монолитный продукт вполне себе успешно живёт и приносит прибыль.
Самый большой и важный пункт - параллельная разработка. Реализация или изменение бизнес-процесса, рефакторинг, обновление зависимостей на общий API, оптимизация, исследования, документирование и всё что в принципе способен делать разработчик - может происходить параллельно в множестве микросервисов. И возможность параллельной работы здесь это не самоценность, а способ быстрой доставки бизнес-ценности.
Параллельная разработка в микросервисах быстрее таковой в монолите потому что последнее это, как правило, работа над несколькими разными бизнес-процессами, отличающихся по сложности и времени реализации. Часто эти доработки не влезают в спринт, набираются по остаточному принципу и из необходимости занять всех разработчиков в команде.
В то же время параллельная разработка в микросервисах - это реализация одного и того же бизнес-процесса, с одновременными изменениями в нескольких местах и фокусом всей команды на реализации этого функционала.
Если вы спросите всех известных вам владельцев продукта, что они выбрали бы по результатам спринта - три возможно готовых доработки или одна точно работающая от и до, которую можно посмотреть самому и показать стейкхолдерам (может быть и с некоторой обратной связью от пользователей, либо результатами с продуктивной среды), то я уверен что второй вариант будет самым популярным ответом.Как вы знаете, самая большая проблема разработчиков - это другие разработчики. И чем больше разработчики мешают друг другу, тем хуже и тем дольше необходимо разбираться с последствиями. Самый любимый способ разработчиков мешать друг другу - любые изменения кодовой базы, которые вступают в конфликт с изменениями других разработчиков. По своему опыту вы знаете что есть множество причин, по которым можно потратить от пяти минут до нескольких дней, любуясь diff окошками с изменениями из нескольких веток, периодически привлекая тех, с чьими изменениями вы конкурируете. Это больно бьёт по скорости работы команды и стабильности приложения.
Самый простой вариант не решать конфликты при параллельных изменениях в одной кодовой базе - не делать параллельных изменений в одной кодовой базе. В случае с микросервисами всегда есть чем заняться параллельно в разных сервисах, не мешая друг другу, и при этом приносить пользу команде и продукту.Для того, чтобы изменения разработчика попали в кодовую базу, последний, в числе прочего должен пройти процедуру ревью своего кода и выйти из неё победителем. В общем случае процедура преследует три цели - поиск дефектов, проверка стиля написания кода, и предварительное согласование этих изменений с параллельными изменениями других разработчиков. В монолите каждая из этих основных, и других побочных причин напрямую влияет на качество кодовой базы. А качество кодовой базы напрямую влияет на качество продукта. А всё, что напрямую влияет на качество монолитного продукта должно подвергаться тщательному досмотру на месте.
И поэтому разработчик обязан пройти процедуру ревью своего кода перед тем как код попадёт в общую кодовую базу. А ещё это значит что кто-то обязан провести ревью. И лучше получить лайки от нескольких людей. Таким образом мы попадаем в ситуацию, когда код написан, но мы ждём пока кто-нибудь поставит эти лайки. И просим людей отвлечься от реализации своих фич (возможно, чуть более приоритетных, чем наша), или от исправления дефекта. К сожалению провести в таком состоянии можно до нескольких дней, что в условиях привычного двухнедельного спринта является очень и очень большим сроком.
Здесь можно было бы пошутить о том, что быстрее всего пройти ревью можно если не проводить ревью вообще, но нет. Ревью это нужный и важный процесс работы команды. Только в случае с микросервисами ревью можно безопасно проходить в неблокирующем режиме. Можно сделать изменения в коде, провести функциональное тестирование, влить изменения в релизную ветку, найти дефекты и влить исправления по его результатам. Можно передать сервис тестировщикам и пройти регресс. Можно отправить доработку в продуктивную среду и получить обратную связь от пользователей. Можно ждать реализации частей большой доработки в соседних микросервисах. А пока идёт регресс, или вы ожидаете других разработчиков, или функционал работает в продуктиве - вы можете пройтись хоть по всем участникам вашей команды и получить ревью от них. По результатам ревью внести изменения и снова отдать сервис на регресс, а потом снова отправить в продуктив.
Ожидание ревью без готовой фичи и ожидание ревью с готовой фичей отличаются, как минимум, на одну готовую фичу.-
Начать любую работу с кодовой базой в микросервисном подходе легче, чем в монолитном. И речь идёт не только о исправлении дефектов или реализации бизнес-процессов, но и о том, что со временем и опытом формируется в желание усовершенствовать продукт, но откладывается или встречает отказ и разногласия в команде продукта по разнообразным причинам - сложно, долго, рискованно, сейчас фокус на другом, не вовремя, и так далее, и тому подобное.
Относительная легкость принятия решения о внесении любых изменений в микросервисах обусловлена двумя факторами:Масштаб изменений. Гораздо легче и быстрее изменить проект состоящий из пары десятков классов, чем из пары сотен. Такие изменения быстрее проходят ревью. На такие изменения проще согласиться, будучи техническим руководителем проекта, или его проще убедить в их необходимости. Такие изменения легче запланировать в ближайший набор работ. Рефакторинг ? Да. Попробовать тестконтейнеры ? Запросто. Перевести все запросы с jpa на jdbc ? Легко. Переписать весь сервис за спринт ? Заверните два.
Цена ошибки. Сломать одну из шестерёнок механизма кажется менее пугающей перспективой, чем целиком неработающий механизм. Мы не боимся сломать какую-нибудь важную функцию в нашей цепочке, потому что если вдруг такое случается - мы просто запускаем прошлую стабильную версию сервиса, отшучиваемся о технических работах в ЦОДе и спокойно разбираемся с причиной проблемы. При монолитном подходе мы не можем просто запустить прошлую версию сервиса, так как вместе с нашим дефектом откатятся соседние доработки и исправления других дефектов, что выльется в каскад проблем. Это обязывает нас исправлять ошибку здесь и сейчас, формируя очень настороженное отношение к будущим изменениям, не получившим оценку необходимости “нужно было ещё вчера”.
Таким образом, при маленьком масштабе изменений и низкой цене ошибок мы, с одной стороны, получаем постоянный поток мелких модификаций поддерживает продукт в хорошей технической форме, и оставляющий возможность экспериментировать в поиске наиболее эффективных решений. С другой стороны, команда может гораздо быстрее реагировать на обратную связь от владельца продукта, реализуя не только жизненно необходимый, но периферийный и экспериментальный функционал.
Микросервисы гораздо более комфортно чем монолиты укладываются в продукты, развивающиеся по гибким методологиям. Скорость появления доработок, командный фокус на одной задаче, оценка работы в маленьких сервисах, возможность написать необходимый микросервис и отказаться от него через несколько спринтов а также многое другое органично укладывается в концепцию быстро растущего продукта под внимательным наблюдением заказчиков.
В итоге, начиная реализовывать проект в парадигме микросервисной архитектуры, мы получаем возможность параллельно разрабатывать необходимый функционал небольшими изменениями с низкой ценой ошибки, максимально предотвращая пересечения разработчиков и не блокируя путь от реализации до работы функционала.
Конечно, кроме этого на качество и скорость разработки продукта влияют и другие факторы - уровень компетенций команды, зрелость ci/cd, автоматизация тестирования и прочее. Но, здесь работает достаточно простое правило - всё что может быть автоматизировано и настроено в монолите - может быть настроено и в микросервисах.
Спасибо, что дочитали до конца!
Надеюсь, этот небольшой материал поможет хотя бы одному продукту в индустрии стать немного лучше.
Комментарии (4)
Apoheliy
25.06.2022 15:12+1Предлагаю переименовать статью так:
Микросервисы (иногда) разрабатывать быстрее по гибким методологиям, если микросервисы вам подходят.
С остальным можно спорить. Например:
Параллельная разработка делается и на монолитах. Не нужно думать, что монолит это один длинный файл исходного кода. Обычно там есть изолированные модули, библиотеки, компоненты ...
Пункт 2, 3 (с учётом первого) - может быть спорно.
Пункт 4 - наверное да, верно. Хотя, если задать вопросом документирования API, protobuf-ов, протоколов (то, что в микросервисах тоже делается), то порог входа можно сильно облегчить.
Пункт 5 - да, укладывается. В гибкую.
По моему: Микросервисы хороши, когда они в сетевой доступности для программиста: хочешь - поменял; хочешь - доработал. В остальных случаях (редкая поставка, нет доступа к продукту 24/7) это может быть боль.
Т.е. это не вопрос скорости разработки. Скорее: подходит ли вам микросервис (в том числе и по желаемому качеству)?
Nohwan
27.06.2022 09:58Писать распределенные системы - сложнее, и автоматически требует решения нескольких классов проблем, отсутствующих в крупных/монолитных сервисах, что повышает требования к квалификации разработчика и добавляет новые требования к квалификации devops. Даже ключевые апологеты подхода сейчас рекомендуют дробиться по мере необходимости, а не начинать дробиться сразу;
Качественная изоляция модулей в сервисной архитектуре (без "микро"), реализованной в том числе как "цитадель" или распределенный монолит, позволяет достигнуть примерно тех же результатов, что описано в тексте, путем версионирования модулей, backward и forward совместимости и поэтапной выкатки;
Выделение микросервисов оправдано при уникальном профиле нагрузки соответствующего сервиса, требующего иных подходов к техническому стеку и масштабированию. В противном случае, качество инженерных практик существенно важнее степени дробления проекта;
AndreySu
Первые несколько пунктов не проданы, как минимум.
Паралельная разработка микросервисов возможна при согласованном контракте между ними и соответственно между командами/разработчиками их разрабатываемыми. А если так то что мешает согласовать в монолите в виде контракта между несколькими разработчиками интерфейсы взаимодействия внутренних компонент приложения. На выходе получим ровно такую же ситуацию.
sshikov
Именно так. При этом замечу, что уже много-много лет не видел больших монолитов, которые бы не содержали модули/компоненты в том или ином виде — их просто невозможно написать, если не думать об этом вообще (хоть в терминах микросервисов, хоть в каких). То есть, так или иначе, а компоненты или модули в монолите есть, и все что применимо к микросервисам, по большей части применимо и к ним. Ну т.е., условно, где-то в 2005 году приложение у меня уже выглядело как J2EE контейнер (несколько штук), в который установлены несколько штук модулей, общающихся между собой либо через API, либо через очереди (как правило API не REST, т.е. без преобразования внутренней модели классов во что-то внешнее, а напрямую — как удаленный вызов, который для разработчика выглядел как локальный). И эти самые компоненты или модули разрабатывались командой параллельно друг с другом. Их API нужно было конечно согласовывать — что и делалось в процессе каждого релиза, когда соседним командам сообщали об изменениях.
>мы просто запускаем прошлую стабильную версию сервиса
Да ну, правда что-ли? А мы еще в weblogic где-то в 2010-м умели деплоить несколько версий сервиса одновременно, при этом можно было старую выключить, новую включить (разумеется, есть нюансы типа занимаемых сервисом портов, например), и так же плавно вернуться обратно. То есть, я бы сказал, что я так делаю постоянно, уже лет 15 как. И никаких микросервисов при этом у меня в разработке нет. Разумеется, есть разница — например, тогда нельзя было запустить N экземпляров в разных контейнерах. Но ведь автор же не про это.