世界を動かす技術を、日本語で。

「dry-run」の賛美

概要

  • 新しいレポート作成アプリケーション の開発体験
  • --dry-runオプション の導入経緯と活用事例
  • テスト・検証時の利便性 と具体的な使い方
  • コードへの影響 やデメリットの考察
  • --dry-runの有用性 と導入タイミングの重要性

--dry-runオプション導入の背景と経緯

  • 新しい レポート作成アプリケーション の開発プロジェクト
  • アプリケーションは 平日ごとに自動でレポートを生成 し、複数の工程を実施
    • データベースからのデータ読込
    • レポート生成ロジックの適用
    • レポートのzip圧縮・SFTPサーバーへのアップロード
    • SFTPサーバーからのエラーレスポンス確認・解析
    • 通知メールの送信
    • ファイルの工程別ディレクトリ移動
  • 開発初期に SubversionやLinuxコマンドの--dry-runオプション を思い出し、同様の機能を組み込むことを決断
  • --dry-run指定時は 各フェーズの処理内容を出力 し、実際には変更を加えない
    • 生成するレポートや処理対象ファイルの一覧表示
    • SFTPサーバーへのアップロード・ダウンロード予定ファイルの確認

--dry-runオプションの具体的な効果と活用例

  • 日常的に--dry-runを活用 し、作業前の安全確認や動作チェックを実施
  • --dry-runは 安全に実行可能 なため、意図せぬ変更を防止
  • 各種設定やシステム状態の 即時確認・サニティチェック 用途
  • システム全体のテスト時にも 素早いフィードバック を得る手段として有効
    • 例:レポート状態ファイルの日付変更後、即座に生成対象かを出力で確認
    • 実際のレポート生成を伴わないため、 結果確認が高速

--dry-run導入によるデメリットと実装上の注意点

  • dryRunフラグの存在がコードを多少複雑化
    • 各主要フェーズで dryRunフラグの判定 が必要
    • ただし、実際のレポート生成など深い処理内部では判定不要
    • 処理呼び出し前の判定 のみで十分

--dry-runオプション導入の総括

  • 今回のような コマンド実行型・バッチ処理アプリケーション には--dry-runが非常に適合
  • リアクティブ型アプリケーション (イベント駆動型)にはあまり適さない
  • 開発初期から--dry-runを導入 したことで、機能追加時にも継続的な恩恵
  • --dry-runは 全てのプロジェクトに適する機能ではない が、適用できる場面では極めて有用

Hackerたちの意見

普段は逆のことをして、CLIユーティリティに--reallyフラグを追加するんだ。デフォルトは読み取り専用にして、何かを間違えるにはちょっと手間がかかるようにしてる。

同じこと言いに来た。

"--i-meant-that"をコミットしたことがあるよ(リモートマシンを壊すコマンド用で、通常はメッセージを表示して10秒間^Cを押すかどうか考える時間を与えるんだ)。特にせっかちな同僚のためにね。結局、不適切に使われることはなかったからラッキーだったけど(でも、どれだけ運が良かったかは数えなかったよ)。

コードベースを汚さずに動かすためには、永続性を注入可能な戦略に移さなきゃいけないって分かった。それが逆に良い結果を生むんだよね。if dry_run:をあちこちに渡してたら、マジでやばいことになるよ。正直言うと、テスト実行のために人に--dry-runを実行させるより、プロダクション実行には--wet-runを使った方がずっといい。実際のものを誤って起動する可能性が低くなるからね。

デザインパターンが役立つ場面だね、みんながそれに対して目を roll するけど。

もしできるなら、アプリケーションが取るアクションを明示的にモデル化して、それを実際に処理する中央のものに渡すのがいい方法だよ。そうすれば、ドライランかどうかを理解する必要があるのはコードの一箇所だけになる。理想的には、コアロジックからそれを返すだけで済む、"機能的コア、命令的シェル"スタイルがいいね。

「--wet-run」って音があんまり好きじゃないけど、何度か「dry-run」がデフォルトで、実際に変更を加えるには「--no-dry-run」が必要なツール(たまにサービスも)を作ったことがある。サービスに関しては、どこで動いてるかを自動で検知してほしいな。つまり、開発環境で動いてるなら、デフォルトで開発用のDBを使うって感じ。

毎回「rm --wet-run tempfile.tmp」とか「mkdir -p --yes-really-do-it /usr/local/bin」って打ちたくないんだよね。プログラムは、頼んだことをちゃんとやるのがデフォルトであるべきだと思う。一方で、すべてのツールに「--undo」引数があって、最後にやったことを元に戻せるといいな。

自分がメンテしてる(内部の)CLIでは、REST APIを呼び出すコードの中にif not dry_run:を入れてる。HTTPコールをCURLコマンドとしてログに記録する設定があるから、ドライランモードでは実際に呼び出さずにどんなHTTPコールをするかを確認できるんだ。これがうまくいくのは、CLIコマンドが単一の操作を実行する場合、例えばこのREST APIを呼び出す時だけど、少しでも複雑なことをし始めると、例えばAPI1を呼び出してその結果をAPI2に送るみたいなことになると、かなり難しくなる。もちろん、API1が返す可能性のあるものをシミュレーションすることはできるけど、急にif not dry_run:よりもずっと複雑でエラーが起こりやすいものになるんだよね。

1つの場所(もしくは一般的にそれを制限すること)がその処理をすることで、dry_runチェックがコード全体を汚染するのを防げる。自動化パイプラインでヘッドレスVMが動かすCLIツールをたくさん管理してるけど、ほぼすべてのツールでこれをやってるよ。

コンソールツールにRESTやHTTPにこだわるの、なんでなの?!RESTの肥大化はマジでヤバい。最近の子供たちは、すべてをIP/TCP/httpsで動かしたがるんだよね。なんで?!まずはローカルツールを書くことを学ぼうよ。

逆のやり方も好きだな。-commitや-executeみたいに、デフォルトで実行するのが不変だと仮定することで、バリデーションの複雑さを簡素化して、ライブ実行を明示的にするんだ。

Hacker Newsで議論の続きを見る