В начале проекта обычно есть одно окружение — прод. Все правки катятся туда, тестирование идёт на локалке, в худшем случае — «на пользователях». Это работает быстро, на это не нужно тратить время, и кажется, что ничего больше и не нужно.

Так бывает ровно до первого инцидента. Миграция базы прошла не так, как ожидалось. Переменная окружения потерялась после деплоя. Новая версия упала через два часа после релиза, а старого артефакта уже нет. После такого опыта обычно появляется мысль: «надо как-то отделить экспериментальное от рабочего».

Разберём, как организовать dev/stage/prod так, чтобы это помогало работе, а не превращалось в отдельную инфраструктурную нагрузку. Особенно — для небольшой команды, у которой нет лишних рук на DevOps.

Что обычно идёт не так с одним окружением

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

Поправил вёрстку в проде, пропустил баг. Маленькое изменение — что может пойти не так. Закоммитил, задеплоил, ушёл с работы. Утром выяснилось, что на половине устройств кнопка перестала кликаться, и об этом узнали из жалоб в поддержку.

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

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

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

Каждая из этих историй решается одинаково: иметь место, где можно проверить изменение до того, как оно попадёт в прод.

Что делает каждое окружение

Окружения — это не про абстрактное «разделение». Это три разные роли в процессе разработки.

Development (dev)

Где живёт. Локалка разработчика или общий dev-сервер.

Зачем нужно. Писать и проверять код в процессе разработки. Делать эксперименты, ломать, перезапускать.

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

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

Staging (stage)

Где живёт. Отдельная инсталляция, максимально похожая на прод по конфигурации.

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

Особенности. Своя база — обезличенная копия прода или синтетические данные. Свои домены — типа stage.yourdomain.com. Свои ключи внешних сервисов — sandbox-режим, тестовые webhook’и. Свой набор переменных окружения. По возможности — те же ресурсы, что в проде, чтобы поведение совпадало.

Кто туда катит. Либо CI автоматически из ветки stage или develop, либо разработчик вручную, когда нужно проверить конкретное изменение.

Production (prod)

Где живёт. Продовая инфраструктура.

Зачем нужно. Обслуживать реальных пользователей.

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

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

Сравнительная таблица

Чтобы свести разницу в одно представление:

devstageprod
ДанныеФейковые или синтетическиеОбезличенная копия прода или синтетикаРеальные пользовательские
Доменlocalhost или dev.yourdomain.comstage.yourdomain.comapp.yourdomain.com
СекретыТестовые ключиSandbox-ключи внешних сервисовПродовые секреты
Кто деплоитЛюбой разработчикCI из ветки stage или вручнуюCI из main после проверки
МониторингОпциональноБазовыйПолный с алертами
Что делать при паденииПерезапуститьПочинить и перенакатитьОткатиться немедленно

Практические правила, которые экономят боль

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

1. Никогда не используйте продовые секреты в dev и stage

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

На практике это значит: для каждого внешнего сервиса заводите отдельные ключи на каждое окружение. Sandbox-ключи API, тестовые webhook’и, отдельные SMTP-аккаунты для отправки тестовых писем (или вообще не отправляйте — пишите в файл).

2. Базы данных у каждого окружения свои

Не подключайте dev-приложение к продовой базе даже на чтение. Любая ошибка в коде — забытый DELETE WHERE без условия, неправильный JOIN, миграция в неверном направлении — попадёт в прод-данные.

Структура схемы (DDL) у всех окружений одинаковая. Данные — разные. На stage может быть обезличенная копия прода (без персональных данных) или сгенерированная синтетика, главное — чтобы объёмы и распределения были близки к реальным, иначе stage перестаёт быть репетицией.

3. Миграции прокатываются сначала на stage

Главное правило, которое отличает зрелый процесс от незрелого.

На stage у вас всегда должна быть свежая копия прод-схемы — это важно, потому что миграции часто ломаются на реальных данных, которых нет в dev. На stage прокатили — увидели, сколько по времени, что ничего не упало, что приложение после миграции работает. Только после этого катите на прод.

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

4. Домены и URL должны явно отличаться

app.yourdomain.com для прода, stage.yourdomain.com для stage, dev.yourdomain.com или localhost для dev.

Это не косметика. Когда домены явно разные, проще не отправить тестовый email десяти тысячам пользователей, не списать с реальной карты при отладке, не настроить интеграцию мимо нужного окружения. Если все три окружения открываются по одному и тому же URL с переключением через cookie или header — рано или поздно кто-то перепутает.

5. Переменные окружения — отдельный набор для каждого окружения

Имена переменных одинаковые, значения разные. DATABASE_URL, API_KEY, WEBHOOK_SECRET — на dev, stage и prod разные значения.

В коде никогда не должно быть хардкода ни для одного из окружений. Если ловите себя на «ну тут на stage всегда один и тот же URL, я просто впишу» — не вписывайте, заведите переменную. Через полгода кто-нибудь (возможно, вы сами) полезет менять и не найдёт.

6. Доступы к окружениям разные

В прод имеют право катить определённые люди или CI. В stage — все разработчики команды. В dev — автор изменения.

В небольшой команде «определённые люди» может означать «один человек или CI». Это нормально, главное — что катить в прод не может кто угодно случайно. Один из самых частых источников инцидентов — разработчик, который думал, что подключён к stage, а на самом деле к проду.

7. Мониторинг на всех окружениях, но с разной чувствительностью

На проде — алерты в реальном времени. Упало — узнали сразу, не от пользователей.

На stage — алерты тоже, но мягче: ошибки на stage нужны для разработки, а не для тушения пожаров. Алерт уровня «пишите в общий канал, когда удобно».

На dev — логи доступны, но никаких алертов: иначе команда будет получать уведомления о собственных экспериментах посреди ночи.

Когда хватит двух окружений вместо трёх

Чтобы статья не звучала догматично — три окружения нужны не всем.

Без stage можно жить, если:

  • проект очень маленький, один разработчик;
  • это статический сайт без backend и базы;
  • стратегия «сломалось — починим за 5 минут» приемлема для бизнеса;
  • внешних интеграций нет или они мокаются;
  • миграций базы практически не бывает.

В этом случае dev + prod без stage — рабочая модель. Не нужно усложнять то, что не требует усложнения.

Stage становится обязательным, как только в проекте появляется одно из:

  • регулярные миграции базы данных;
  • платежи или другие необратимые операции;
  • внешние интеграции с реальными последствиями (отправка email, SMS, push-уведомлений, webhook’и в чужие системы);
  • команда больше одного человека;
  • SLA или ожидания пользователей по доступности.

Если узнаёте свой проект в этом списке — пора заводить stage.

Как это устроено в TatNet

В TatNet организация окружений сводится к нескольким настройкам.

Каждое окружение — отдельный проект на платформе. У него свой набор ресурсов, своя база, свой VPC, свой домен. Это значит, что окружения изолированы по-настоящему: упавший stage не валит prod, эксперимент в dev не дотянется до продовых данных.

Один репозиторий — разные ветки на разные окружения. Push в main катит prod, push в stage катит stage, push в feature-ветку может поднимать preview-окружение для проверки конкретной фичи.

Переменные окружения и секреты — на уровне каждого проекта. Имена одинаковые, значения свои. Случайно перепутать prod-токены с stage-токенами архитектурно невозможно.

Домены подключаются отдельно. app.yourdomain.com к продовому проекту, stage.yourdomain.com к stage. HTTPS на каждом — автоматический.

Доступы — на уровне проекта в кабинете. Можно настроить, кто из команды может катить в prod, а кто только в stage. Без раздачи SSH-ключей и общих паролей.

Создать окружения в TatNet →

Итог

Окружения — это не enterprise-история и не обязательная сложность. Это инфраструктурный аналог git-веток: возможность работать над разным параллельно, не мешая работающему.

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

Один раз настроили dev/stage/prod — дальше процесс разработки становится предсказуемым. Эксперименты не ломают прод, миграции проверяются перед накатом, релизы перестают быть стрессом, и пятница снова становится обычным рабочим днём.

Создать окружения в TatNet →