Фиксация соглашений в команде

Соглашения — важная часть процесса разработки. Они призваны снижать затраты и упрощать жизнь сотрудников. Проблема в том, что часто соглашения, наоборот, только всё усложняют, поскольку они толком не зафиксированы и передаются исключительно из уст в уста, как старые сказки. В процессе передачи они обрастают новыми подробностями, а прежние подробности упускаются. В итоге у каждого участника команды в голове свой набор соглашений, которые он периодически забывает.

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

В этой статье я расскажу, как фиксировать соглашения правильно, чтобы они приносили вам пользу. 

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

Как же добиться того, чтобы соглашения помогали, а не мешали? Их нужно фиксировать. И делать это так, чтобы:  

  • было удобно пользоваться;  

  • на следование этим соглашениям требовались минимальные трудозатраты;  

  • легко было понять, какие соглашения актуальны;  

  • легко было понять, почему приняты именно такие соглашения;  

  • они были автоматизированы (в идеале).

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

  • соглашения на уровне кода;  

  • соглашения на уровне архитектуры;  

  • соглашения на уровне процессов.

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

Соглашения на уровне кода

bb7300ad83bc4dad62ecb2a9da5195f4.png

Эти соглашения направлены на то, чтобы код выглядел единообразно, понятно и читаемо. Вот несколько примеров таких соглашений:  

  • Используем одинарные кавычки для строк вместо двойных.

  • Не обращаемся к ENV напрямую из кода, за исключением одного класса, где оборачиваем вызовы к ENV в методы.

  • Сервисные объекты имеют в конце названия слово Service и имеют один публичный метод call.

Такие соглашения преследуют одну прямую цель — снизить когнитивную нагрузку на читающего код разработчика, в том числе помочь ему быстрее ориентироваться в незнакомом коде. Как писал Мартин, код гораздо чаще читают, чем пишут, — разница больше чем в 10 раз. Как бы вы ни относились к Rails, в этом фреймворке на полную работает потрясающий принцип convention over configuration, что позволяет любому разработчику на Rails открыть чужой проект и сразу же довольно неплохо в нём ориентироваться.

Как фиксировать такие соглашения? Линтером. Если подходящего соглашения нет в правилах линтера, напишите собственный линт. Практически все линтеры позволяют это делать: вот пример для Go, а вот — для Ruby.

Фиксация подобных соглашений линтером даёт сразу три преимущества:  

  • Разработчик не думает об этих правилах, линтер тут же подсветит все проблемы, а зачастую ещё и сам их поправит. 

  • Если вы используете код-ревью, люди на нём не будут работать линтерами, а посмотрят на более важные вещи.

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

Небольшой бонус: написание правила для линтера — отличная практика для джуна. Пока он с ним разбирается, узнает больше о том, как парсится код, как строится AST, лучше поймёт язык.

Соглашения на уровне архитектуры

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

  • Используем язык Python для написания обычных сервисов и Elixir в нагруженных частях системы.

  • Бэкэнд отдаёт ошибки в таком-то формате.

  • Каждый сервис обязан отдавать метрики в prometheus, на эндпоинте /metrics, порт для отдачи метрик конфигурируется переменной окружения PROMETHEUS_PORT.

Такие соглашения, помимо снижения когнитивной нагрузки, решают ещё три задачи:  

  1. Снижают эксплуатационные издержки. Если сервисы единообразно запускаются, одинаково отдают логи, одинаково отдают метрики, то гораздо проще обслуживать сервис и разбираться с инцидентами. 

  2. Снижают издержки на проектирование. Разработчику не нужно каждый раз проектировать архитектуру с нуля — вы заранее подумали, и теперь нужно спроектировать только конкретную фичу/сервис, не беспокоясь о базовых вещах. 

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

Такие соглашения более сложные, и я предпочитаю фиксировать их в два шага.

Шаг 1 — описываем

Architecture Decision Record (ADR) — инструмент фиксации подобных соглашений. Его прелесть в том, что он фиксирует не только соглашение, но и сопутствующую информацию: почему было принято такое соглашение; какие альтернативы обсуждали; когда соглашение последний раз пересматривалось; актуально ли соглашение сейчас.

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

ADR состоит из нескольких основных блоков:  

  1. Какую проблему решает соглашение.

  2. Какие варианты решения проблемы рассматривались, их плюсы и минусы.

  3. Какой вариант выбрали в итоге.

Могут быть и дополнительные блоки — например, расчёт стоимости внедрения выбранного решения.

ADR удобнее всего вести там, где видно историю изменений и обсуждений. Мой выбор — это Github и Notion, у каждого есть свои плюсы и минусы. Преимущество Github — в наличии «из коробки» инструмента для ревью и отслеживания истории версий. Notion привлекает удобством работы с базами данных в нём, тегами и тем, что с ним легко справятся и непрограммисты.

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

Шаг 2 — автоматизируем

ADR сложнее автоматизировать, чем соглашения по коду: линтера для дизайна, кажется, пока не придумали (а жаль!). Тем не менее отчасти автоматизировать возможно и их, в зависимости от того, что это за соглашение.

Для соглашений по языкам, библиотекам, встраиванию сервисов в инфраструктуру и тому подобному создавайте и обновляйте шаблоны сервисов. Тогда при создании нового сервиса разработчик не пишет его с нуля, а копирует из шаблона и сразу получает настроенный Dockerfile, отдачу метрик и т. д.

Точно так же можно делать и генераторы классов внутри одного приложения. Например, если вы договорились о нескольких слоях (контроллер => форма => сервис-объект), можно сделать простую консольную команду, которая будет генерировать сразу все слои для новой фичи.

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

Соглашения на уровне процессов разработки

1ff93be53614a4ced72c10829dd86b29.png

По процессам в каждой компании есть множество соглашений, например:  

— Описание того, как устроен наём в компанию. 

  • Описание процесса выкатки релиза. 

  • Наличие дизайн-ревью для больших задач.

  • Проведение синка два раза в неделю с обсуждением текучки.

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

Идею я подсмотрел у Виталия Шароватова на TeamleadConf Foundation 2022. К сожалению, видео его выступления пока недоступно, так что опишу кратко суть.

Виталий предложил инструмент, аналогичный ADR, — Process Decision Record (PDR). Единственное отличие — вместо архитектурных решений в нём описываются решения о процессах. Кроме того, он предложил в каждом PDR ставить «дату переосмысления» — когда вы возвращаетесь к документу, чтобы понять, решает ли он всё ещё ваши проблемы лучшим образом, через n месяцев после принятия (к слову, то же самое можно делать и с ADR).

Что касается автоматизации процессов, с ней всё сложно. Какие-то процессы вы можете автоматизировать, настроив worflow в jira, поставив напоминания для встреч или создав бота, который автоматически готовит презентацию итогов недели (я так делал, правда в чужой компании).

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

И что в итоге?

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

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

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

Кроме того, есть и другие соглашения, не описанные мной в статье. Например, вы можете фиксировать соглашения по дизайну в виде библиотеки компонентов.

Для каждого нового типа соглашений проходите три этапа:  

  1. Придумайте, как его фиксировать.

  2. Придумайте, как его автоматизировать.

  3. По прошествии времени убедитесь, что соглашение экономит вам больше ресурсов, чем тратится на его поддержание.

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

© Habrahabr.ru