10 октября 2026
Что показал аудит
- Разобраться с секретами в истории Git. Нашлись удаленные из текущего кода, но все еще действующие секреты. Часть из них могла остаться у бывших сотрудников. Я рекомендовал отозвать и перевыпустить эти учетные данные, а хранение секретов вынести из репозиториев.
- Наладить культуру CI/CD. Сборка и деплой были автоматизированы, а запуск тестов зависел от того, вспомнит ли о нем разработчик. Основные ветки содержали разные изменения, совместимость frontend и backend проверялась вручную. Я предложил встроить проверки в процесс выпуска и определить порядок продвижения изменений, чтобы меньше зависеть от человеческого фактора.
- Восстановить запуск проекта из чистой копии. Это условие для запуска проверок в CI и для подключения новых сотрудников. Штатный сценарий не работал из-за проблем в окружении и миграциях. Нужен один поддерживаемый способ запуска без ручного изменения исходников.
- Исправить работу с деньгами. Списание и возврат запускались из callbacks ActiveRecord, обращение к платежному шлюзу происходило внутри транзакции базы, а в проверенных вызовах не было ключей идемпотентности. Это оказалось главной находкой аудита. Позже заказчик подтвердил реальные последствия: повторные списания и потерянные платежи. Расхождения приходилось распутывать через жалобы клиентов и поддержку. Я предложил выделить явные платежные операции и добиться идемпотентности платежных операций, определить обработку ошибок и сверку состояния шлюза и базы.
- Упростить расчет стоимости. Расчет был собран в god object на 1747 строк с общим изменяемым состоянием. Тарифы, промоакции, скидки, налоги и дополнительные услуги влияли друг на друга. Разработчики уже боялись трогать этот код. Заказчик подтвердил, что ошибки после релизов приводили к ночным подъемам для срочных исправлений. Предложил постепенно выделять правила расчета в чистые функции, используя существующие тесты.
- Довести проверки контракта между frontend и backend до рабочего состояния. OpenAPI-схема уже была, но автоматические проверки на ее основе оставались недоделанными. Работа шла последовательно: сначала backend, затем frontend, а несовпадения выявлялись при соединении частей. Я предложил согласовывать контракт до реализации, генерировать из него типы TypeScript и проверять их при сборке. Критические пользовательские сценарии нужно покрыть e2e-тестами.
- Инвестировать в AI SDLC после исправления проблем с CI и кодовой базой. Запрос на AI сейчас звучит часто, но в этой команде сначала нужно наладить проверки, запуск проекта и работу с критической бизнес-логикой. Польза AI SDLC зависит от инженерной культуры: понятных задач, надежной проверки результата и ответственности за изменения.
Как я к этому пришел
Ко мне обратился оффлайн-бизнес, работающий в нескольких штатах США. Сам бизнес достаточно крупный, но команда разработки очень компактная. Она поддерживает продажи, клиентский портал заказов и платежи.
Запрос был общий: кажется, разработка идет долго. Пробуют канбан и Scrum, вводят DoR и DoD, но заметного результата нет. Попросили посмотреть, что мешает и что еще можно сделать.
На аудит было жесткое ограничение по времени: две встречи с заказчиком, продуктом и разработчиками и доступ к основным репозиториям. Я сосредоточился на выпуске изменений, оформлении заказа, расчете стоимости и оплате. Без доступа к production, мониторингу и истории задач я не мог проверить время ожидания решений, частоту инцидентов и состояние бэклога.
Удаленный секрет остается в истории
Я проверил историю Git. В ней нашлись секреты, удаленные из текущего кода, но все еще действующие. Удаление файла не отзывает учетные данные и не убирает их из предыдущих версий. Часть этих данных могла остаться у бывших сотрудников, у которых раньше был доступ к репозиториям.
Поэтому рекомендовал отозвать и перевыпустить действующие секреты, проверить доступы и определить способ их хранения вне репозиториев. Этот вопрос поставил первым по срочности.
Проверки были, но не участвовали в выпуске
На backend был актуальный стек и большой набор содержательных тестов. Команда поддерживала их вместе с кодом. Расчет стоимости проверялся на конкретных сценариях и суммах.
Это был хороший фундамент для улучшений. Но в конфигурации CI тесты не запускались. Разработчик должен был выполнять их самостоятельно, и результат зависел от того, не пропустил ли он этот шаг. Я предложил сделать проверки частью общего процесса выпуска.
Я попробовал поднять проект из чистой копии. Штатный запуск не сработал: пришлось разобраться с окружением, конфигурацией базы и миграциями. Та же проблема мешала запускать тесты на чистом CI runner и усложняла подключение нового сотрудника. После ручной подготовки тесты удалось запустить. Часть падений требовала разбора с командой.
Поэтому предложил сначала восстановить воспроизводимый запуск, затем подключить тесты к CI. Перед включением блокирующей проверки нужно классифицировать падения и устранить причины непредсказуемого результата.
Команда использовала GitFlow с отдельной staging-веткой. При проверке истории выяснилось, что часть изменений из релизной линии отсутствовала в develop. Это типичная проблема hotfix: исправление попадает в production, но не возвращается в основную ветку разработки, если процесс не требует и не проверяет этот шаг. Я предложил сверить ветки, вернуть необходимые изменения и закрепить обязательный возврат hotfix в develop.
Отладку и автоматизацию CI я считаю одним из самых выгодных вложений для этой команды: содержательные тесты уже есть, нужно встроить их в выпуск. Это основа для более быстрой и надежной доставки изменений и дальнейшего внедрения AI SDLC.
Как история коммитов помогла найти проблемный участок
Я посмотрел, в каких частях системы чаще появляются исправления. Заметная часть была сосредоточена вокруг заказа, промоакций, скидок, расчета стоимости и платежей.
Это помогло выбрать, где подробнее изучить код. Число исправляющих коммитов я не использовал как число инцидентов в production: один коммит может исправлять несколько проблем, а сообщения об изменениях не всегда позволяют определить их причину.
В разговоре заказчик подтвердил, что именно здесь регулярно возникают сложности. До аудита он не связывал их с устройством кода. Разбор показал две разные проблемы: выполнение финансовых операций и сложность расчета стоимости.
Работа с деньгами
Списание и возврат запускались из callbacks ActiveRecord. Обращение к платежному шлюзу происходило внутри транзакции сохранения записи. В проверенных вызовах я также не нашел ключей идемпотентности.
Сохранение заказа могло запустить внешнюю финансовую операцию. Если платеж прошел, а запись не сохранилась, откат транзакции базы сам по себе не отменяет списание. При повторе операции нужно отдельно понимать, как система определяет, что платеж уже выполнен.
Меня это особенно удивило. По моему опыту, такое устройство скорее ожидаешь в очень молодом продукте, где торопятся собрать MVP. Другой возможный контекст: есть отдельный учет и сверка платежей, которые позволяют обнаруживать и разбирать расхождения. Тогда последствия могут быть менее критичными. Наличие такого механизма здесь нужно было уточнить; по репозиториям я не мог оценить весь финансовый контроль.
Этот пункт оказался главной находкой аудита. Позже заказчик подтвердил, что были и повторные списания, и потерянные платежи. О расхождениях узнавали через жалобы клиентов, затем вместе с поддержкой разбирались, что произошло с деньгами. Это были реальные последствия, а не только риски, которые я увидел в коде.
Я предложил стандартную схему обработки платежей: фиксировать промежуточные шаги операции, использовать один ключ идемпотентности при повторных обращениях к шлюзу и блокировки для защиты от одновременной обработки. После сбоя можно восстановить состояние операции и продолжить ее с учетом уже выполненных шагов. Это локальное исправление с большой ценностью для бизнеса: снижает риск повторных списаний и потерянных платежей, защищает деньги и доверие клиентов. Его влияние на NPS можно оценить после внедрения.
Расчет стоимости
В Price::Calculate было собрано 1747 строк логики: базовая цена, сезонные условия, скидки, промокоды, налоги, комиссии и дополнительные услуги.
Это был god object с общим изменяемым состоянием. Порядок вычислений и изменения внутренних данных связывали правила между собой. Чтобы поправить одно из них, приходилось разбираться в соседних расчетах и проверять, что они не пострадали. Разработчики уже боялись трогать этот участок. В последующем обсуждении заказчик подтвердил: после релизов ошибки в расчетах приходилось срочно исправлять, в том числе ночью.
При этом расчет стоимости принципиально можно организовать как чистую функцию. На вход передаются данные заказа и применимые правила, на выходе получается итоговая сумма и ее разбивка. Получение данных, сохранение заказа и платежные действия остаются за пределами расчета.
Я предложил двигаться в эту сторону небольшими шагами: выделить одно правило, явно описать его вход и результат, проверить существующие сценарии, затем переходить к следующему. Тесты цен давали основу для такого рефакторинга.
Почему frontend ждал backend
Основа для решения уже была в проекте: OpenAPI-схема (Swagger) и зачатки контрактного тестирования. Реализацию не довели до конца, поэтому совместимость по-прежнему проверяли после соединения frontend и backend. Это было одной из причин затягивания реализации функций и хрупкости релизов: несовпадения приходилось разбирать, когда обе части уже были написаны.
Я предложил довести эту схему до рабочего инструмента: согласовывать запросы и ответы до реализации, генерировать из OpenAPI типы TypeScript и проверять их при сборке frontend. Несовместимые изменения тогда будут останавливать сборку. Frontend сможет работать с тестовыми данными по согласованному контракту, а backend проверять соответствие реализации схеме.
Полноценных e2e-тестов тоже не было. Проверка типов не покажет, может ли пользователь оформить заказ и оплатить его. Автоматические проверки критических сценариев от начала до конца я считаю еще одним важным вложением в качество проекта: они помогут находить ошибки во взаимодействии частей до релиза.
Крупные задачи также делились преимущественно по техническим слоям. Работа шла, но показать законченный результат можно было только после соединения backend и frontend.
Здесь предложил попробовать декомпозицию по пользовательским сценариям. Каждый небольшой срез проходит через необходимые слои и дает результат, который можно проверить и обсудить. Для подходящих изменений release flags позволят интегрировать код и включать новое поведение для ограниченной группы.
Начинать стоило с одной задачи и посмотреть, получится ли раньше показать результат и уменьшить объем переделок.
Что выяснилось о процессе
В обсуждении обозначилась проблема: после готовности первоначального объема появлялись новые пожелания, а ожидание решения продлевало исходную задачу.
Нужно было уточнить:
- что входит в первую законченную версию;
- кто принимает результат;
- сколько времени задача может ждать решения;
- как оформляются новые пожелания после демонстрации.
DoR и DoD могут закрепить часть этих договоренностей. Но нужно проверить, помогают ли они начинать работу с понятными требованиями, принимать результат и закрывать согласованный объем.
Я предложил фиксировать готовность к разработке, начало работы, готовность к проверке и выпуск. Это поможет отделить реализацию и переделки от ожидания.
Достаточных оснований рекомендовать другую методологию у меня не было. Сначала стоило проверить причины задержек в текущем процессе.
Сначала инженерная культура, затем AI SDLC
В команде уже были отдельные эксперименты с AI-инструментами. Но инвестировать в перестройку SDLC под агентов я предложил после исправления проблем с CI и кодовой базой. В первую очередь нужны воспроизводимый запуск, автоматические проверки и понятное устройство критических операций.
Запрос на AI сейчас звучит часто. В этом проекте его польза зависела от того, насколько команда умеет ставить задачи, проверять изменения и отвечать за результат. Если эти практики не работают, ускорение написания кода добавляет нагрузку на ручную проверку и разбор ошибок.
После этих исправлений можно провести одну задачу через записанные требования, план, реализацию с агентом и проверку. Результат стоит оценивать по всей задаче: подготовке, реализации, проверке и переделкам.
Что осталось после аудита
Обсудили с заказчиком результаты аудита, ситуацию на рынке и команду. Он согласился с выводами и счел аудит полезным. Немного разобрали варианты решений и возможные изменения в устройстве команд.
Подтвержденных результатов внедрения у меня пока нет. На этом этапе результат моей работы: диагностика и план изменений.
Асинхронно получил еще один отзыв:
Женя красавчик, передавай ему привет еще раз