Пятница. 18:37.
Рабочая неделя почти закончилась. Кто-то уже захлопнул ноутбук, кто-то обсуждает планы на выходные, а ты наконец дописал ту самую задачу, которая несколько дней висела в работе.
Тесты зелёные. На локальной машине всё работает. Код выглядит вполне прилично. Осталось совсем немного — выкатить в production.
Неопытный разработчик в такой момент сомневается. Уверенный нажимает Deploy. Особенно если впереди два выходных.
В конце концов, если ты действительно уверен в своём коде, какая разница — пятница сегодня или вторник?
Меня в этой сцене всегда занимал не день недели, а сама уверенность. На чём она держится: на ощущении «я хорошо написал» или на системе, которая заметит ошибку и не даст ей испортить выходные всей команде?
Лучшее время для релиза — когда все уже ушли
У вечернего деплоя есть очевидные преимущества. Никто не отвлекает сообщениями «а что с задачей?», нагрузка может быть ниже, а если что-то пойдёт не так, впереди целых два дня, чтобы спокойно разобраться.
Ещё это отличный способ проверить качество процессов в компании. Если обычный релиз способен уничтожить выходные, возможно, проблема вовсе не в пятнице.
Но к этому неприятному выводу мы вернёмся позже. Пока правило простое: закончил задачу — выкатывай. Не надо превращать релиз в отдельное инженерное мероприятие.
CI/CD нужен только большим компаниям
Небольшому PHP-проекту совершенно необязательно строить pipeline. Есть проверенная последовательность:
git push
↓
ssh production
↓
git pull
↓
composer install
↓
php artisan migrate
↓
php artisan optimize
Работает же. Особенно если деплоит один человек и прекрасно помнит, в какой каталог зайти, какую ветку переключить, какой .env нельзя перезаписывать и каких workers надо перезапустить после обновления.
Есть только два небольших недостатка. Все знания живут в голове разработчика, а одна и та же ручная последовательность каждый раз выполняется немного по-разному.
Сегодня он вводит composer install --no-dev --optimize-autoloader. В прошлый раз запускал без флагов. Через месяц забудет перезапустить Horizon. Зато никакой бездушный YAML не ограничивает инженерное творчество.
CI/CD нужен не потому, что команда большая. Production просто не должен зависеть от памяти человека. Pipeline превращает «я вроде помню» в воспроизводимый маршрут:
push → analysis → tests → build
↓
совместимая migration
↓
активация новой версии
↓
restart Horizon workers
↓
smoke + health checks
И пятничный вечер сразу становится менее увлекательным.
Если тесты зелёные — значит, всё работает
Это одна из самых удобных инженерных концепций: Tests passed, следовательно, Production works. Особенно если проверки покрывают только часть того, что происходит в production.
Тесты отвечают на конкретный вопрос: работает ли код в условиях, которые мы предусмотрели? Production спрашивает иначе: что случится на реальных данных, при медленном внешнем API, очереди в несколько тысяч сообщений, недостатке памяти, долгом SQL-запросе или нескольких инстансах приложения вместо одного?
CI — первая линия защиты, не нотариальная гарантия. После deployment начинается другая работа: health checks, метрики, логи, error rate, latency и бизнес-показатели.
Но уверенный разработчик в метриках не нуждается. Он ведь написал хороший код.
Мониторинг начинается с жалобы пользователя
Пользователи — прекрасная система наблюдения. Если сайт перестал работать, они обязательно сообщат. Обычно так: «У вас что-то сломалось». Иногда добавят полезную деталь: «Уже минут сорок».
Реактивный мониторинг строится просто: пользователь замечает ошибку, пишет менеджеру, менеджер ищет разработчика, разработчик открывает логи. Есть менее социальный маршрут: ошибка → мониторинг → алерт → разработчик. Пользователь из цепочки почему-то исчезает.
Наличие prod.log ещё не делает систему наблюдаемой. Лог рассказывает, что приложение решило записать. Для эксплуатации хочется знать больше: отвечает ли сервис, сколько запросов завершается 5xx, выросла ли latency, живы ли workers, растёт ли очередь, не заканчивается ли диск и доступны ли внешние зависимости.
Причём технического «200 OK» недостаточно. Checkout может отдавать успешный ответ и не создавать заказы. Uptime прекрасный, бизнес — не очень. Поэтому рядом с error rate иногда важнее видеть orders per minute, успешные платежи и конверсию.
Хороший алерт сообщает обо всём
Чтобы ничего не пропустить, настроим побольше уведомлений: CPU выше 60%, потом 65% и 70%; память выше 70%; Redis reconnect; каждый 404; очередь длиннее 10, 20 и 30 сообщений. Всё — в один Telegram-канал.
Через неделю команда приобретёт важный production-навык: не замечать алерты.
Alert fatigue особенно быстро появляется из-за большого количества сообщений, которые не требуют действий. Полезный алерт говорит не «что-то технически изменилось», а «вероятно, человеку пора вмешаться».
Например: доля 5xx выше 5% пять минут подряд; очередь десять минут растёт быстрее, чем обрабатывается; успешность checkout заметно упала относительно нормы. Из сообщения должны быть понятны затронутый сервис, критичность, время начала и первый dashboard для расследования.
Иначе мониторинг становится дорогим генератором тревожности.
Rollback — это git revert
Допустим, невероятное всё-таки произошло: после релиза production начал падать. Ничего страшного. Есть же git revert.
Кроме случаев, когда версия изменила схему данных, формат сообщений очереди, API, кеш, конфигурацию или зависимости.
До релиза приложение читало users.name. Новая версия разделила его на users.first_name и users.last_name, перенесла данные и удалила старое поле. Через пять минут выяснилось, что регистрация сломана. Откатываем код — и старая версия снова ищет колонку, которой уже нет.
сломали новой версией
↓
git revert
↓
сломали старой версией
Rollback — не команда, а свойство архитектуры релиза.
Скучный путь называется expand / migrate / contract: добавить новые поля, научить код понимать обе схемы, перенести данные, переключить чтение, убедиться, что старая схема не используется, и удалить её отдельным релизом. Дольше. Зато новая и старая версии некоторое время совместимы, а откат остаётся реальным действием, не пунктом в презентации.
Feature flags придумали те, кто боится выкатывать код
Если функция закончена, её нужно сразу включить всем. Иначе зачем мы её разрабатывали?
Так deployment автоматически становится release. Если новый checkout ломается, откатываем всю версию вместе с остальными безопасными изменениями.
Feature flag разделяет два события:
code deployed
↓
feature OFF
↓
команда
↓
5% пользователей
↓
25%
↓
100%
Если метрики портятся, rollout останавливается, а функция выключается без отката всей сборки. Ошибка затрагивает часть аудитории вместо всех сразу.
Это не отменяет тесты, staging и code review. Ремень безопасности тоже не разрешает ехать с закрытыми глазами.
У флагов есть собственный риск: забытые переключатели превращают приложение в набор альтернативных вселенных. Поэтому у каждого временного флага должен быть владелец и дата удаления: создали, раскатили, стабилизировали, удалили.
Один человек должен знать, как всё выкатывается
В каждой компании есть такой разработчик. Назовём его Сергей.
Сергей знает, где лежат production-конфиги, почему второй worker нельзя перезапускать раньше первого, какой cron иногда зависает, зачем миграцию №247 запускать вручную и какой Redis можно очищать, а какой лучше не трогать.
Перед релизом команда задаёт один health check: «Сергей сегодня на месте?»
Процесс прекрасен до первого отпуска Сергея. Или болезни. Или увольнения.
Release process должен быть документирован и автоматизирован настолько, чтобы ответом на вопрос «кто умеет выкатывать?» не было имя одного человека. А рядом с важным алертом нужен короткий runbook: что проверить, где dashboard и логи, как восстановиться, когда эскалировать.
Это не документация ради документации. Это способ не проводить одно и то же расследование заново, пока пользователи ждут. Особенно в пятницу вечером.
Так можно ли деплоить в пятницу?
Конечно. Календарь не содержит технического свойства, из-за которого PHP после 18:00 хуже выполняет код.
Опасность начинается там, где скорость считают от написанного кода до production, а не до стабильной пользы. Пятиминутный deployment с трёхчасовым incident после него быстрым не был.
Перед релизом я бы задал несколько скучных вопросов.
До релиза
- проверки прошли, а deployment воспроизводим;
- миграция совместима со старой версией;
- риск изменения понятен, rollback действительно возможен;
- большая функция закрыта флагом;
- известно, какие технические и бизнес-метрики должны измениться.
Во время и после
- есть health checks и smoke-проверки;
- rollout можно остановить;
- видны error rate, latency и состояние очередей;
- алерты доходят до человека, который может отреагировать;
- релиз считается завершённым после подтверждения метрик, а не после зелёной надписи Deployment successful.
Если на эти вопросы есть ответы, пятничный deployment может быть обычным событием. Если процесс выглядит как ssh → git pull → composer install → «вроде работает» → ушёл домой, то во вторник он ненамного безопаснее.
Хороший deployment должен быть скучным: pipeline прошёл, canary работает, метрики в норме, rollout завершён. Через несколько минут команда занимается другими задачами. Огромное количество инфраструктуры строится именно ради этой неинтересности.
Вместо вывода
Мой антисовет прост: деплойтесь в пятницу вечером. Особенно если rollback никогда не проверяли, мониторинг состоит из tail -f, а единственный человек, который знает production, уже выключил телефон.
Это быстрый способ выяснить, работает ли CI/CD, замечаете ли вы проблему раньше пользователей, совместимы ли миграции с предыдущей версией, проверен ли сценарий rollback или roll-forward и существует ли release process за пределами головы Сергея.
Правда, проверять всё это непосредственно в production необязательно.
Хороший процесс не обещает, что ошибок больше не будет. Он обещает другое: ошибку быстро увидят, последствия ограничат, а систему безопасно вернут в рабочее состояние.
И тогда пятничный deployment действительно становится классикой уверенного разработчика. Только уверенность эта уже не в собственном коде, а в системе вокруг него.