26 июл. 2024 г.·7 мин чтения

Маленькая инженерная команда: как поддерживать глобальный софт с меньшей рутиной

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

Маленькая инженерная команда: как поддерживать глобальный софт с меньшей рутиной

Почему больше людей не всегда помогает сделать работу

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

Зарплата — лишь часть расходов. Каждый дополнительный сервис добавляет алерты, дашборды, шаги релиза и новые способы сломаться в 2 часа ночи. Если одно действие клиента затрагивает пять систем, у маленькой ошибки теперь есть пять мест, где можно спрятаться.

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

Поэтому загруженные команды часто чувствуют перегрузку, даже когда выпускают совсем немного. Они часами повторно запускают неудачные деплои, разбирают шумные алерты, объясняют старые решения и снова чинят одни и те же слабые места. Это настоящая работа. Просто это не работа над продуктом.

Есть и другой способ всё организовать. Олег Сотников показал это на практике в AppMaster, где команда полного размера превратилась в гораздо более маленькую AI-first-операцию, а стабильность при этом осталась почти идеальной. Такой результат появился не потому, что людей попросили работать усерднее. Он появился благодаря более простой архитектуре, меньшему количеству пересекающихся инструментов и понятному владению.

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

Где маленькие команды теряют время

Большинство маленьких команд в первую очередь выгорают не от нехватки сил. Они выгорают от нехватки внимания.

Работы может и хватать на команду, но постоянное переключение — нет. Когда один продукт использует три backend-языка, два front end-стека и набор разрозненных сервисов, любое изменение становится медленнее. Ошибка в биллинге втягивает кого-то в Python, следующая задача требует TypeScript, а скрипт деплоя всё ещё живёт в Bash. Никто не остаётся достаточно сильным во всех этих областях. Небольшие пробелы превращаются в длинные паузы, пока люди заново воссоздают контекст.

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

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

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

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

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

Выберите архитектуру, с которой ваша команда реально справится

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

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

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

Держите путь обработки запроса коротким. Если действие пользователя проходит через gateway, потом через два API, потом через очередь, worker и кеш, инциденты становятся медленными и путаными. Короткие цепочки проще отследить, протестировать и объяснить следующему инженеру, который придёт в команду.

Следите за тихим дублированием. Команды добавляют второй кеш, потому что один endpoint тормозит, второй job runner, потому что первый кажется неаккуратным, или отдельный сервис для отчётов, который постепенно становится обязательным. Так в работу незаметно просачивается еженедельная рутина. Если два инструмента делают почти одно и то же, выберите один и уберите другой.

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

Назначьте каждому элементу понятного владельца

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

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

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

Пишите имя владельца рядом с самим объектом. Храните это в репозитории, runbook или каталоге сервисов. Должно быть легко ответить на несколько базовых вопросов: кто первым разбирает инцидент, кто одобряет деплой, кто проверяет изменения схемы, кто может менять алерты и кто подменяет на время отпуска.

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

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

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

Сузьте стек инструментов

Поддержка Fractional CTO
Получите помощь senior-специалиста до того, как будете нанимать новых людей.

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

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

Разрастание языков создаёт похожее замедление. Каждый язык добавляет свои package tools, правила lint, стиль тестирования, особенности runtime и нагрузку на найм. Для крупной компании со специализированными командами это может быть оправдано. Для маленькой группы, которая круглосуточно поддерживает продукт, это обычно плохая сделка.

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

Общие шаблоны помогают больше, чем ожидают команды. Хороший шаблон для API, фоновой задачи или внутреннего админ-инструмента убирает десятки мелких решений. Логирование работает одинаково. Health checks работают одинаково. Настройки деплоя выглядят знакомо. Код не идентичный, но форма одна и та же.

Именно такую дисциплину Олег Сотников использует в AI-first operations: небольшой набор инструментов для CI/CD, observability и деплоя вместо кучи пересекающихся сервисов. Это делает ежедневную работу спокойнее, потому что команда решает проблемы внутри одной знакомой системы.

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

Убирайте дубликаты быстро. Два инструмента мониторинга, два job runner’а или два способа управлять секретами обычно означают двойное обслуживание и размытое владение. Стандартизация не выглядит эффектно, но она работает. Скучные системы ломаются реже, а когда всё-таки ломаются, кто-то знает, куда смотреть первым.

Простой способ сократить рутину за 30 дней

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

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

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

Цикл очистки на 30 дней может быть очень простым:

  1. Неделя 1: составьте инвентарь и назначьте владельцев.
  2. Неделя 2: посчитайте оповещения, неудачные деплои и повторяющиеся тикеты.
  3. Неделя 3: напишите runbook для трёх проблем, которые возвращаются чаще всего.
  4. Неделя 4: уберите один инструмент или процесс, который добавляет работы и почти ничего не даёт взамен.

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

Runbook должны быть короткими. Обычно одного экрана достаточно: что сломалось, как проверить, как восстановить и когда эскалировать. Если уставший инженер не может быстро пройти по инструкции, runbook слишком длинный.

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

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

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

Разберитесь с архитектурой
Получите помощь в объединении лишних частей и сокращении пути от клика до базы данных.

У небольшой SaaS-компании были клиенты в Европе, Азии и США. При трёх инженерах кто-то всегда был почти на дежурстве. Один человек следил за клиентским приложением, один занимался биллингом, а один поддерживал админские инструменты, но на практике всех троих постоянно втягивали в одни и те же инциденты.

Главной проблемой был не трафик. Проблемой было количество движущихся частей. У команды было два отдельных background worker’а, у каждого — своя логика повторных попыток, логи и сценарии отказа. Обновления они выкатывали через разные пути деплоя, а когда ситуация казалась срочной, люди всё ещё делали ручные изменения на серверах.

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

Они изменили меньше, чем обычно ожидают команды. Объединили два background worker’а в одну простую систему задач, перевели все деплои в один pipeline, перестали менять серверы вручную и назначили каждой области одного понятного владельца, хотя в экстренной ситуации помогать по-прежнему мог каждый.

Результат проявился уже через несколько недель. Меньше очередей — меньше крайних случаев. Один путь деплоя означал, что production соответствует тому, что они тестировали. Когда задача падала, логи были в одном месте, а откат занимал минуты вместо ночи догадок.

Ночные алерты стали реже, потому что одновременно ломалось меньше частей. Биллинг-инженеру больше не нужно было смотреть логи app worker’а. Инженер по приложению перестал присматривать за дрейфом серверов. Админские инструменты стали скучными, а это именно то, чем внутренние инструменты и должны быть.

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

Ошибки, которые добавляют работу каждую неделю

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

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

Разъезд фреймворков создаёт более тихий хаос. Если каждый инженер выбирает свой фреймворк, команда платит за такую свободу каждый день. Code review замедляются, исправления багов занимают больше времени, а передачи работы превращаются в повторное обучение. Узкий стек проще нанимать, проще документировать и гораздо проще поддерживать в 2 часа ночи.

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

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

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

Прежде чем нанимать ещё одного инженера

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

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

Проведите жёсткий аудит, прежде чем открывать вакансию. Попросите одного инженера проследить путь клиентского запроса от первого клика до базы данных. Если он не может объяснить этот путь без догадок, проблема не в количестве людей. Система просто сложнее, чем должна быть.

Потом проверьте, как быстро команда может выпустить небольшое изменение. Дайте более новому коллегe маленькую правку с обычным review и деплоем. Если он всё ещё не может безопасно выкатить её за один день, настройка слишком хрупкая. Команды замедляются, когда окружения отличаются, шаги деплоя живут у кого-то в голове, или rollback неясен.

Короткий чеклист ловит большую часть потерь:

  • Один инженер может объяснить путь запроса через приложение, задачи и базу данных.
  • Более новый коллега может выпустить низкорисковое изменение за день и не спрашивать помощи у трёх человек.
  • Алерты подсказывают дежурному инженеру, что делать дальше, а не просто сыпят шумом в чат.
  • У каждого сервиса есть один владелец и один путь деплоя.
  • Команда может назвать работу, которая повторяется каждую неделю.

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

Если эти проверки не проходят, сначала чините систему. Ваш следующий найм должен добавлять мощность, а не унаследовать путаницу.

Что делать дальше

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

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

Потратьте следующую неделю на устранение трения, прежде чем писать план найма. Посмотрите, где работа стопорится. Посчитайте, как часто кто-то просит доступ, ждёт деплой, ищет логи или дважды чинит одну и ту же проблему в настройке. Это не мелкие неудобства. Они тихо съедают целые полдня.

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

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

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

Если вам нужен внешний взгляд, Олег Сотников занимается такой работой Fractional CTO и startup advisory через oleg.is. Он помогает стартапам и малому бизнесу упрощать архитектуру, инфраструктуру и AI-driven development, чтобы команды работали быстрее и с меньшими накладными расходами.

Сначала сделайте уборку. Потом решайте, нужен ли вам ещё один инженер.