Чек-лист хороших инженерных практик в компаниях

Чек-лист хороших инженерных практик в компаниях

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

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

Причин такой катастрофической разницы довольно много. Вот некоторые из них:

  • Ошибки топ-менеджмента в бизнесе. Если бизнес делает не то, что надо, то не важно насколько эффективно он делает это — в конце концов бизнес закроется. Эта тема выходит за рамки текущего гайда.
  • Ошибки топ-менеджмента в области процессов. Если на этом уровне все плохо, то все остальное вторично. Даже неверная система бонусов может привести к разладу в команде и полной блокировке разработки в конечном счете.
  • Человеческий фактор. Личные качества и человеческие пороки могут создать проблемы как остальным членам команды, так и всему проекту в целом. Главная проблема в том, что эту часть невозможно выправить никакими процессами. Только изменение поведения. Либо расставание.
  • Плохой процесс разработки. Эта тема касается всех инженеров без исключения. Сюда входит все, начиная от взаимодействия и работы с задачами, заканчивая тестированием и проведением ревью кода.

На некоторые проблемы повлиять либо сложно, либо невозможно (с уровня разработчика). Но другие, особенно относящиеся к инженерным практикам, нужно постоянно улучшать и менять. Программисты должны принимать в этом самое активное участие.

  • Книги
    • Человеческий фактор. Успешные проекты и команды
    • Мифический человеко-месяц, или Как создаются программные системы
    • Идеальный программист. Как стать профессионалом разработки ПО
    • Цель. Процесс непрерывного совершенствования
  • Manifesto for Agile Software Development
  • Bus Factor
  • Карго культ

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

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

Код

Хорошо

  • VCS. Код находится под контролем версий (как правило гит).
  • Общий код. Любой член команды в любой момент времени может изменить любую часть системы.
  • Единый стиль кода. В команде все придерживаются стандартов кодирования, принятых для данного стека (языка, платформы).

Плохо

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

Ссылки

Среда разработки

Хорошо

  • Девелопмент среда. Разработка ведется в специальной development (dev) среде. Как правило, это локальная машина (возможно, с использованием Vagrant или Docker Compose). Эта среда у каждого разработчика полностью своя, и изменения в одной среде не могут влиять на другие среды разработки.
  • Разворачивание среды автоматизировано и происходит "одной кнопкой". Это позволяет легко вводить в проект новичков, быстро и в автоматическом режиме распространять инфраструктурные изменения, работать без страха что-либо поломать, так как легко восстановить.
  • Инфраструктура как код. Распространение изменений конфигурации происходит через код проекта. Достаточно еще раз выполнить развертывание дев среды (с новым кодом проекта), как подхватятся все обновления.
  • Среда разработки максимально приближена к условиям продакшена. Если сервис работает на Linux, то и разработка ведется на Linux. То же самое касается и других аспектов.

Плохо

  • Разворачивание среды и настройка происходит по мануалам, либо методом "попробовал запустить — прочитал сообщение об ошибке — погуглил — исправил". Дорого и неэффективно. Мануалы устаревают практически сразу после того, как их пишут. Новый человек может тратить дни на разворачивание среды с нуля.
  • Ручное обновление конфигурации. Всем разработчикам рассылается директива произвести локальные изменения настройки среды (например, доставить что-нибудь новое) для работы нового кода.
  • Общая база данных для всех разработчиков. Нагрузка от одного человека влияет на всех. Случайная поломка также тормозит всех остальных.

Качество

Хорошо

  • Кодовая база покрыта тестами. Тесты повышают уверенность в работоспособности кода. Хорошие тесты положительно влияют на дизайн самого кода. Как правило, код, покрытый тестами, сам по себе лучше кода без тестов. Хотя есть корреляция.
  • Частично протестированная фича или вовсе — фича без тестов — не считается выполненной. Наличие тестов значительно снижает нагрузку на всех остальных членов команды и положительно влияет на качество решения задачи. К тому же часто происходит, что если тесты не написать сразу, то потом на них времени не останется.
  • Программист отвечает за фичу до самого конца. Фича считается выполненной, только когда она работает на продакшене. Каждый человек в команде должен понимать, что наиважнейшая цель — это доставка ценности клиенту. Пока фичей никто не пользуется, то не важно, написана она или нет, потому что бизнес в этот момент остается в пролете.
  • Команда ревьювит код друг друга (без фанатизма). Ревью — не только способ найти ошибки, но и способ учиться друг у друга.
  • Парное программирование. Техника эффективна не только между программистами. Она очень полезна в парах "программист и тестировщик", "новичок и опытный".
  • Continuous integration (CI). Репозитории проекта подключены к серверу непрерывной интеграции, на котором после каждого коммита проверяется стиль кодирования (через запуск линтеров), прогоняются тесты, осуществляется сборка проекта (например, компиляция).
  • В случае инцидентов проводятся пост мортемы.
  • Ретроспектива. Процесс непрерывно улучшается и на изменения влияет каждый член команды.

Плохо

  • Нет тестов. Работа нового кода проверяется только ручным способом, через прокликивание. Последствия катастрофические — скорость доставки низкая, а качество кода, скорее всего, неудовлетворительное.
  • Отсутствует код ревью. Разный стиль кодирования, изоляция программистов друг от друга, слабый обмен опытом, плохие решения в продакшене.
  • Программист считает, что фича закрыта, когда код попал в основную ветку. Новый код лежит мертвым грузом и не приносит пользы. Может устареть до попадания клиенту.
  • KPI. Активно используются количественные метрики: строки кода, выпущенные фичи, закрытые баги. Вместо ориентации на результат, разработчики стремятся выполнить KPI. Даже в случае, если это идет вразрез с задачами бизнеса.
  • Высокий уровень формализации процессов. Замедляется скорость, падает мотивация.

Ссылки

Процесс разработки

Хорошо

  • Разработчики руководствуются принципами 12factors. Приложения проще разворачивать, масштабировать и мониторить.

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

  • Тесты писать легко и приятно. Лакмусовая бумажка для определения того, насколько хорошо с тестами в проекте. Если приходится себя заставлять, то есть вероятность, что тесты написаны плохо (например, много моков) и их будет недостаточно.

  • Test-driven development (TDD). По возможности тесты пишутся до кода. Есть несколько причин, по которым это важно:

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

    Код в любом случае надо проверять. Если теста не будет, то это придется делать руками.

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

Плохо

  • Тесты есть, но приходится заставлять себя писать тесты, потому что их сложно писать, они долго выполняются, часто ломаются или постоянно приходится их переписывать.
  • Запуск одного теста занимает секунды. Такой тест тяжело запускать при разработке через тесты и общее время выполнения тестов становится слишком большим.
  • Код правится прямо на продакшене (то место где он работает). Без комментариев.

Ссылки

Выкатка новых версий (более актуально для веб-проектов)

Продакшен-среда — инфраструктура (например, сервера), в которой развернут проект. Она обеспечивает доступ к проекту конечным пользователям.

Деплой (выкатка) — процесс, в рамках которого происходит обновление проекта в продакшен среде.

Хорошо

  • Автоматизация. Развертывание автоматизировано и выполняется одной кнопкой.
  • Частые небольшие релизы. Развертывание — рядовое событие, которое может выполняться в любой момент по готовности фич, без необходимости отвлекать команду.
  • Zero Downtime Deploy. Обновление версии происходит прозрачно для пользователей.
  • Развертывание, технически (то есть все хорошо автоматизировано), может выполнить любой член команды.

Плохо

  • Выкладка происходит в ручном режиме. Например, через прямое управление с сервера. Самый ненадежный и не масштабируемый подход, подвержен ошибкам и может занимать значительное время. При наличии нескольких серверов просто не работает.
  • Выкладка кода сопровождается эмоциональным напряжением и вовлечением большого числа участников. В такой атмосфере все стремятся выкатываться реже, что приводит к еще большим проблемам и напрямую вредит бизнесу.
  • Процесс развертывания длится десятки минут или часы. Скорее всего, это означает, что процесс сборки проекта интегрирован с самим деплоем. Эти задачи нужно выполнять независимо.
  • Разворачивание происходит раз в неделю и реже. Чем больше изменений выкатывается сразу, тем выше шанс поломки. И тем сложнее отследить влияние каждой фичи на бизнес-показатели. Кроме того, происходит забывание тех изменений, которые были сделаны ранее и ждали своего часа до выхода в продакшен.
  • Во время разворачивания наблюдаются длительные даунтаймы. Пользователи вынуждены ожидать завершения деплоя. Такая ситуация мешает деплоить часто.
  • Развертывание выполняет один специальный человек. Знания хранятся в одной голове. Уход в отпуск или болезнь ломает весь процесс. Остальные программисты не понимают "как оно работает там".
  • Деплой конфигурации. Обновление конфигурации, не относящейся непосредственно к логике кода, требует повторного деплоя. Например, изменение пароля к базе данных или адреса базы. Эти параметры являются чисто инфраструктурными и должны попадать в код так, как описано в 12factors.

Ссылки

Исходный код (github)
Кирилл Мокевнин