探偵の日常
「やあ、探偵君。また難事件を解決したそうだね。どんな事件だったんだい?」
「そうだね、
1つの不可解な殺人。
2つの密室。
4つの消えた凶器。
8つの残されたメッセージ。
それから次々と現れる16人の不審な関係者に
彼らの間に結ばれた32の密約。
まあ、概ねいつも通りといったところだったね
」
「そりゃまた謎だらけだね。君はそれをどうやって解決したんだい?」
「簡単なことだよ、」
……
動かしてみないと分からない?
「このコードを変更したらどうなるかは、動かしてみないと分からない」。
レガシーコードを扱っていると、よく耳にする言葉である。この言葉には欺瞞が含まれている。読むだけで挙動を確信することができないコードは、実際に動かしてみたところで、本当にシステムの挙動を理解できるとは限らないからだ。むしろ、何が起きたのかを観測できないコードでは、実行は新たな事実を生み出すだけで、その意味を理解することはできない。
設計としてのオブザーバビリティ
例えば、あるAPIが本番環境で500エラーを返したとする。その事実だけでは、DBアクセスが失敗したのか、外部APIがタイムアウトしたのか、想定外の分岐に入ったのかは分からない。実行結果だけでなく、処理経路、コンポーネント間の呼び出し、外部システムとの通信、状態変化などが観測できて初めて、「何が起きたか」を知ることができる。
ここにオブザーバビリティの設計上の意味がある。
オブザーバビリティは、単にログ、メトリクス、トレースを出力するための運用機能ではない。システムが実行されたとき、その内部で起きたことを外部から推論可能にするための設計である。
テスト可能なオブザーバビティ
テストファースト開発に馴染んだ人の脳には「テスト==設計」という観念が刻み込まれているだろう。もしオブザーバビリティが設計なのだとしたら、それはテスト可能だと考えられないだろうか?
単体テストは、あるコンポーネントがどのように振る舞うべきかをテストとして記述し、実装がその設計に従っていることを継続的に検証する。
同じことをコンポーネント間の設計にも適用できる。
例えば注文処理について、
注文受付
↓
在庫確保
↓
決済認証
↓
注文確定
という処理構造を設計したとする。この構造をTraceやEventとして観測できるようにし、
OrderRequested
InventoryReserved
PaymentAuthorized
OrderCreated
という観測結果を期待値としてテストする。すると、そのテストは単なるログ出力の確認ではなく、コンポーネント間の設計そのものを検証するものになる。
結合テストとオブザーバビリティテスト
オブザーバビリティテストは結合テストの一環として実行されるが、それぞれの役割は異なる。
結合テストが第一に関心を持つのは「仕様」である。入力に対して期待した業務上の結果が得られるかを検証する。その結果を得るのにどのような経路、どのようなコンポーネントを利用しているかには興味を持たない。一方、オブザーバビリティテストが第一に関心を持つのは「設計」である。どのコンポーネントがどのように協調し、その実行がどのような事実として観測されるべきかを検証する。
つまり、
仕様
↓
結合テスト
設計
↓
オブザーバビリティテスト
という対応になる。
実行可能な設計書は古くならない
テストを実行可能な設計書として扱うことには、静的な設計書にはない利点もある。実装やアーキテクチャが変更されれば、オブザーバビリティテストも変更する必要があるからだ。
静的な設計書は、実装との乖離を自動的には検出してくれない。しかし実行可能な設計書としてのオブザーバビリティテストは、変更によって失敗する。テストの失敗は単なる不具合ではなく、「設計を変更したのに、その変更が観測モデルに反映されていない」というシグナルになる。
テレメトリィをアーキテクチャ情報にする
さらに考えを発展させると、組織内のサービスやコンポーネントが一貫した形式で依存関係を観測できるなら、Telemetryそのものをアーキテクチャ情報として利用できるかもしれない。
つまり、すべてのプロセス、ユースケースにたいするテレメトリィデータを収集し、分析することで
- このDBテーブルを利用しているコードはどれか?
- このAPIを呼び出しているサービスは何か?
- このイベントを購読しているコンポーネントは何か?
といった問いに対し、人間がソースコードを検索するのではなく、観測データから機械的に回答できるような情報基盤を作ることができるのではないだろうか?
ここでは、コードの静的解析、オブザーバビリティテスト、本番Telemetryを組み合わせることもできる。
それぞれの情報は
- 静的解析は「存在し得る依存関係」
- オブザーバビリティテストは「設計上期待される依存関係」
- 本番Telemetryは「実際に発生した依存関係」
を意味する。
それらを比較すれば、設計と実装、さらに実際の運用との乖離を検出できる。
この意味で、オブザーバビリティは「システムを監視する仕組み」から「システム自身に設計を記述させる仕組み」へと拡張できる。
最終的に目指すものは、障害が発生したときに優秀な探偵が必要になるシステムではなく、実行履歴そのものが十分な証拠を残しているシステムだろう。
おわりに
「動かしてみないと分からない」という状況は、避けられない。しかし、その次に「動かしてみたら、何が起きたのか分からない」とならないことは、設計できる。
コードを正しく動かすためにテストする。
そして、動いたコードが何をしたのか追跡できるようにする。
前者だけでは、システムは時間とともに理解不能になっていく。後者までテストファーストで設計することで、システムは実行されるたびに、自分自身の設計と振る舞いについての証拠を残すことができる。
それが「テストファースト・トレーサビリティ」の目指すところである。
探偵の日常の終わり
……
「そりゃまた謎だらけだね。君はそれをどうやって解決したんだい?」
「簡単なことだよ、今回の被害者はひどく几帳面な正確をしていたらしくてね、彼が日々つけていた日記が机の上に置かれていたんだよ。中には今回の事件の経緯、関係者間の因縁、犯行手段から密室の謎まで全部書かれていたから、今は警官たちがそれの記述の裏を取るために各地を飛び回り、僕はお役御免というわけさ。」