Как не продолбать архитектуру в погоне за фичами

8f5a16a9e3f38da1667017f973892747.png?v=1

Я работаю в Miro со дня основания, вначале как фронтенд инженер, сейчас как менеджер core-команд, которые разрабатывают внутреннее ядро канваса и realtime-коллаборации на нём.

Мы очень быстро растём: в пользователях, в размере команды, в количестве выпускаемых фич. Немного фактов за 2020 для контекста:

  • Перешагнули рубеж в 10 миллионов регистраций;

  • Пиковая онлайн-нагрузка за год выросла в 7 раз;

  • Команда разработки выросла в 2 раза (инженеры, продакты, дизайнеры);

Выглядит круто! Но есть нюансы.

Наблюдая за компанией 9 лет, я вижу, что кратное увеличение количества инженеров приводит к падению скорости разработки. Задачи по созданию новой функциональности приводят к костылям. Текущая архитектура не позволяет сделать их правильно, а на рефакторинг времени не хватает. Непонятно кто именно должен брать на себя рефакторинг. Начинаемые архитектурные изменения не доводятся до конца из-за отсутствия фокуса. Порог входа  в код повышается, онбордить новичков становится сложнее. Time to market новых фичей падает. 

Это то, куда может завести разработка с фокусом на новую функциональность, но без выделения достаточного времени на архитектуру. На стадии роста новые фичи часто побеждают рефакторинг по приоритетам.

Так мы сталкиваемся с задачей «Как сохранить скорость разработки фичей и гибкость архитектуры на стадии роста?». Откатимся назад в прошлое и будем пробовать разные подходы чтобы прийти к тому, что есть сейчас.

Выделим 20% времени на рефакторинг и улучшения

И так, ситуация X времени назад. У нас есть несколько фичевых команд, которые разрабатывают новую функциональность. Команды самостоятельно реализуют фичу от начала до конца и могут менять любой необходимый для этого код. У каждой команды свой продакт-менеджер, он управляет фокусом и бэклогом команды. Цель продакт-менеджера — достигать определённых бизнес-метрик.

Между продакт-менеджером и инженерами часто возникает типичный конфликт «качественно» vs «быстро».

Продакт топит за скорость,  инженеры говорят что «тут надо бы порефакторить». С одной стороны, команде нужно проверить гипотезу и вывести функциональность здесь и сейчас. С другой стороны, нужно адаптировать или упростить архитектуру и избавиться от костылей, чтобы в будущем не иметь проблем.

Конфликт интересов возникает не потому, что продакт-менеджеры не понимают ценности качественной архитектуры, а команда не хочет создавать новую функциональность. Проблема в том, что некоторые изменения в архитектуре требуют фокуса всей команды на несколько месяцев, а для этого нужно отказаться от вывода новых фичей. В быстрорастущем продукте сделать это крайне сложно.

Чтобы не проседать в качестве, договорились выделять 20% времени на рефакторинг. Работает плохо, если внедрять поверх большой кодобазы. Помогает немного тюнить код, в контексте которого ты сейчас находишься, но не более.

Проблема подхода в том, что он не позволяет делать системных улучшений, над которыми надо сфокусировано работать несколько недель или месяцев. Подход требует хорошо поставленных процессов в команде, чтобы даже эти 20% не отдавались под фичи.

Но самое главное — это мотивация разработчиков делать качественно, не идти на уступки и не писать временные костыли.

Желание сделать правильно и надолго хорошо работает, пока команда небольшая и все понимают, что притронутся к этому коду ещё не раз. Но когда команда растёт и разработчики начинают специализироваться на конкретных областях, гораздо легче согласиться на компромисс и воткнуть костыль в коде, к которому ты больше не вернешься.

Закрепим ответственность за кодом за конкретными командами

Суть подхода — вся кодовая база делится на логические компоненты, которые закрепляются за конкретными командами. Команды реализуют новые фичи самостоятельно от начала до конца. И могут при наличии компетенций и желания изменить любой необходимый для этого код. Но если ты меняешь код не своего компонента, ты должен получить апрув от его владельца.

Что значит «владеть кодом»:

  • Ревьюить пул-реквесты на любые изменения;

  • Согласовывать технические решения от других команд по большим изменениям;

  • Помогать и консультировать другие команды;

  • Приводить код в соответствие критериям качества, принятым в компании, например, иметь тесты и документацию на код;

  • И, разумеется, исправлять ошибки на проде.

Как это выглядит с технической точки зрения.

В репозитории лежит файлик, в котором замаплены конкретные файлы и папки на конкретные команды. При создании пул-реквеста, если код в файле меняется, все релевантные участники команды автоматически добавляются как ревьюеры. Т.е. мимо команды-владельца не пройдут изменения в коде, за который она отвечает.

Самая большая проблема при внедрении этой практики с уже большой кодовой базой — договориться, кто за что отвечает. Без поддержки от менеджмента инициатива не взлетит.

По ходу распределения компонентов у нас «вскрылось» довольно много кода, который в принципе не ложился на фичевые команды в долгосрочной перспективе.

Что делать? Создадим новый тип команд — core-команды, которые будут отвечать за развитие «фундамента».

Вынесем фокус на архитектуру и системные задачи в отдельные core-команды

Это позволит не смешивать фичевые и системные беклоги в одной команде. Таким образом, у core-команд будет постоянный фокус на развитии архитектуры продукта, а беклогом этих команд будет управлять техлид или архитектор.

Цель core-команд — развитие внутренней платформы, на базе которой остальные команды смогут быстрее и качественнее строить новую функциональность и развивать существующую. 

Примеры задач core-команды:  

  • Упрощение доменной модели данных и предоставление API для неё;

  • Создание внутреннего фреймворка для фичевых команд;

  • Изоляция слоёв приложения;

  • Стабильность и производительность сервиса;

  • Проведение серьёзных архитектурных рефакторингов, которые разблокируют реализацию новой функциональности, недоступной нам сейчас.

Хочу отметить отличие core-команд от инфраструктурных. Инфраструктурные команды отвечают за окружение, выводы релизов и отказоустойчивость горизонтальных сервисов, которые мы используем, например, баз данных. Т.е. инфраструктурные команды почти не сталкиваются с продуктовыми задачами.

Core-команды, работают с бизнесовым кодом и без тесной работы с продакт-менеджментом не могут строить долгосрочных планов.

Окей. Создали команды. Будет ли этого достаточно или что-то может пойти не так?  

Разумеется может. 

Например, бизнес может продавить и превратить core-команду в ещё одну фабрику по созданию фичей, просто потому что на этапе роста они всегда в приоритет. Или команда может погрязнуть в разгребании чужих ошибок и костылей. Или может рефакторить систему, которую больше не планируют развивать. Или может потерять связь с реальностью и разрабатывать сферическую архитектуру в вакууме, которая качественно ничего не изменит.

Есть много способов уйти не туда. Вопрос — как сфокусироваться на важном?

Как понять, за что браться в первую очередь? Когда продукт большой, сложностей много и все они в разных местах, много запросов от фичевых команд на доработку ядра. Важно сфокусироваться не на локальных оптимизациях, а на стратегически важных вещах, которые помогут при игре в долгую. Для этого нужна долгосрочная техническая стратегия, выработанная вместе с бизнесом, которая определит развитие продукта на два года и более.

Создадим техническую стратегию

Стратегия должна помочь core-командам понять, что на самом деле мы собираемся построить не через месяц, а через несколько лет. Не так важно, как это называть: техническая стратегия, архитектурное видение, Painted Picture, просто план, — важнее это формализовать, чтобы перенести идеи из головы в документ и договориться, что все согласны со стратегией и понимают её одинаково.

Техническая стратегия включает три источника требований:

  • Бизнес: видение развития компании, продуктовая стратегия, крупные фичи, которые мы планируем реализовать в будущем, но не можем из-за блокеров в архитектуре;

  • Надёжность и производительность: какой ожидается рост нагрузки, под что стоит оптимизировать производительность, какие метрики для нас приоритетные;

  • Бэклог проблем, которые нас замедляют или мешают жить здесь и сейчас. Удобство разработки. Соответствие качества кода стандартным практикам из индустрии.

Первый черновик стратегии мы получили, запершись на несколько дней в переговорке с наиболее опытными разработчиками, и после нескольких мозговых штурмов создали договорились о следующем:

  • Целевое разбитие на компоненты и слои из которых должно состоять приложения.

  • Зоны ответственности команд: явно договорились, какие команды за какие слои и компоненты отвечают;

  • Целевая модель данных: объекты в системе и взаимосвязи между ними;

  • Перечень крупных изменений в core-компонентах, которые ускорят или упростят разработку, а также разлочат продуктовые фичи.

Следующим шагом мы завалидировали стратегию с техническим и продуктовым менеджментом. Собрали обратную связь, внесли корректировки.

Рассказали стратегию всем командам, чтобы при проектировании новой функциональности они учитывали целевую архитектуру и старались вырабатывать решения которые нас будут приближать к ней, а не отдалять.

Наличие документа, а не устных договоренностей — важный шаг. Документ сделал видение явным, на него можно теперь ссылаться при проектировании и понимать, насколько эта конкретная задача ложится на нашу архитектуру.

А дальше началась регулярная работа по воплощению видения в жизнь. Планирование, реализация, внесение корректировок. И регулярная коммуникация про изменения всем заинтересованным участникам.

Итого

Шаги, которые могут помочь сохранить нужный уровень гибкости и скорости разработки новой функциональности, при условии быстрого роста продукта и размеров команды:

  • Дать фичевым командам выделенное время на работу с техническим долгом;

  • Договориться о политиках владения кодом и превратить это в процесс;

  • Определить долгосрочную продуктовую стратегию и требования от бизнеса: понимаем, насколько текущая архитектура удовлетворяет этим требованиям. Если не удовлетворяет и инвестиций надо много,  т.е. изменения не реализовать силами текущих команд в проектном режиме, то создаём отдельные команды из ребят с хорошим знанием продукта. И эти команды фокусируем на приведение архитектуры к целевой;

  • Целевую архитектуру вырабатываем вместе с бизнесом, фиксируем описание, планируем реализацию;

  • Далее фигачим, регулярно актуализируем видение, не забывая рассказывать о ключевых изменениях всем командам.

По некоторым практикам мы еще в самом начале пути. Например core-команды существуют всего полгода, но благодаря формализированной стратегии уже стало на порядок проще общаться с продакт-менеджерами. А именно договариваться, какие фичи мы можем сделать сейчас, а какие нужно отложить пока не адаптируем архитектуру необходимым образом.

В конце добавлю, что чем моложе проект и меньше размер команды, тем легче вводятся подобные практики. И что универсальных практик нет, и можно пробовать разные приемы направленные на качество, но лучше всего работает старое доброе желание сделать хорошо у всех участников команды.

© Habrahabr.ru