Dry-run как предохранитель между кодом и реальными изменениями

02.02.2026 · 5 мин

Знаете, что общего между Terraform, AWS CLI и моей бабушкой? Все трое перед тем, как что-то сломать, предпочитают спросить: «А точно надо?»

Бабушка спрашивает перед выбрасыванием вещей из холодильника. AWS CLI спрашивает, если добавить флаг --dry-run. А Terraform вообще не делает ничего, пока ты не скажешь terraform apply — до этого он только показывает план в terraform plan.

В статье обсуждается концепция Henrik Warne про --dry-run. Становится понятно, что это один из тех паттернов, которые кажутся «лишней работой» на старте проекта, но спасают репутацию и нервные клетки через три месяца эксплуатации.

Что такое dry-run простым языком

Представьте, что вы пишете скрипт, который чистит старые логи. Он должен пройтись по директории /var/log, найти файлы старше 90 дней и удалить их. Без --dry-run вы запускаете скрипт — и молитесь, чтобы регулярное выражение внутри не сожрало важные данные.

С --dry-run скрипт говорит: «Я бы удалил 147 файлов, включая /var/log/important-production-db.log. Вы уверены?» — и вы материтесь, исправляете фильтр, и только потом запускаете «боевой» режим.

Dry-run (пробный прогон) — это режим, при котором программа показывает, что она собирается сделать, но не делает этого. Никаких побочных эффектов (side effects — изменений в базе данных, файловой системе или отправки писем). Только логи и выход в терминал.

Почему это важнее, чем кажется

Henrik описывал reporting-приложение, которое генерирует отчеты, пакует их в zip, загружает на SFTP-сервер (Secure File Transfer Protocol — способ безопасной передачи файлов по сети) и рассылает уведомления. Он добавил --dry-run в самом начале разработки и использовал его десятки раз в день.

Представим похожую систему — автоматическую рассылку инвойсов. Без dry-run отладка превращалась в ад: каждый тестовый запуск создавал реальные PDF-ки, ложил их в S3, отправлял письма клиентам (ну, «тестовым» клиентам, но всё равно неприятно) и двигал файлы по директориям. Цикл «написал код — проверил — откатил изменения» занимал минуты.

Когда добавляешь флаг --dry-run, всё изменилось. Можно проверить логику генерации отчетов за секунды, не дожидаясь рендеринга тяжелых PDF. Видно, какие именно файлы улетят на SFTP, не подключаясь к серверу. Это как разница между прыжком с парашютом и прыжком на батуте — отрабатываешь движения без последствий.

Выполнение скрипта
──────────────────
Без dry-run:
┌─────────┐  ┌──────────┐  ┌─────────┐
│ Код  │───▶│ Действие │───▶│ База  │
│     │  │ (delete) │  │ 💥   │
└─────────┘  └──────────┘  └─────────┘

С dry-run:
┌─────────┐  ┌──────────────┐  ┌─────────┐
│ Код  │───▶│ Проверка  │───▶│ "Would │
│     │  │ if dryRun: │  │ delete │
└─────────┘  │ log(..)  │  │ 5 rows" │
        └──────────────┘  └─────────┘
           │
           ▼
        ┌──────────────┐
        │ База цела  │
        │   ✓    │
        └──────────────┘
Dry-run работает как предохранитель между кодом и реальными изменениями

Как это реализовать (и почему это не больно)

Главный аргумент против dry-run, который я слышу: «Это же загрязнит код кучей if-ов!» Да, придется добавить проверки. Но, как заметил Henrik, они не проникают глубоко в бизнес-логику.

Вот проверенный паттерн:

  1. На уровне CLI (Command Line Interface — интерфейс командной строки) парсим аргументы и кладем флаг dry_run в конфигурацию
  2. На границе системы (там, где код встречается с внешним миром) проверяем флаг
  3. Глубокая логика (генерация отчетов, расчеты) не знает о dry-run — она просто не вызывается
Архитектура с dry-run
─────────────────────
┌─────────────────────────┐
│  CLI Parser      |
|  --dry-run flag    |
└───────────┬─────────────┘
      |
      ▼
┌─────────────────────────┐
|  Orchestrator     |
|  if config.dry_run:  |
|   logger.info("Would |
|    upload to SFTP") |
|  else:         |
|   sftp.upload()    |
└───────────┬─────────────┘
      |
      ▼
┌─────────────────────────┐
|  Report Generator   |
|  (чистая функция,   |
|  не знает о dry-run) |
└─────────────────────────┘
Проверки сухого пробега остаются на границе системы, не проникая в бизнес-логику

Когда это не работает

Dry-run бесполезен для реактивных систем (reactive applications—тех, что ждут событий или сообщений, а не запускаются командой). Если у вас микросервис, который слушает Kafka-топики и обрабатывает сообщения, флаг --dry-run не имеет смысла — вы не запускаете его вручную каждый раз.

Также сложно сделать dry-run для операций, которые нельзя откатить транзакционно. Например, если ваш скрипт вызывает внешний API с числением денег — даже «тестовый» вызов может списать реальные средства или заблокировать счет. Тут помогут только моки (mocks—заглушки, имитирующие внешние сервисы) в тестовом окружении, а не production dry.

Психологический эффект

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

Без dry-run каждый запуск—это стресс. «А вдруг сейчас удалит production базу? „Стресс ведёт к редким запускам. Редкие запуски ведут к накоплению ошибок. Накопление ошибок ведёт к катастрофе.

Henrik прав: добавлять --dry-run нужно в первый же день проекта. Потом будет лень возвращаться и рефакторить. А когда он уже есть, вы используете его инстинктивно, как git diff перед git commit.

Ссылки