意図せず荷重を支えるようになったもの
(jefftk.com)- もともとは些細または一時的に見えた構造やコードでも、時間がたつにつれてシステムを支える役割を担うことがあり、変更前に現在の依存関係まで確認する必要がある
- Chesterton's Fence は、取り除く前に設置理由を理解せよという有用な原則だが、当初の意図だけを見ると後から生じた役割を見落とす可能性がある
- 自宅の浴室工事中、邪魔物のように見えた垂直スタッドは、クローゼットの仕切りという目的がなくなった後も、不適切な構造変更のせいで2階の荷重の一部を支えていた
- 複雑なコンピュータシステムでも、変更履歴や設計文書は出発点にすぎず、コンポーネントが現在のシステムにどのように統合されているかも併せて見る必要がある
- 古いコンポーネントを削除・修正する際は、過去の設計理由と現在の隠れた役割を同時に確認しなければ、予想外の障害を生むことがある
Chesterton's Fenceが見落としうること
- Chesterton's Fence とは、何かを変えたり取り除いたりする前に、それがなぜ作られたのかをまず理解すべきだという考え方である
- フェンスやドアのように人が作ったものには、たいてい誰かにとって有益だと判断された理由があった可能性が高い
- 完全に無意味に見える設計でも、変更しようとしている人が問題の一側面を見落としているかもしれない
- ただしこの見方は、最初に作った人が意図した役割にだけ注目させるため、その後に生じた新たな依存関係を見過ごすおそれがある
家の修理で見つかった偶然の荷重支持
- 数年前に自宅の浴室を作り直したとき、作業の邪魔になる垂直スタッドがあった
- そのスタッドはもともとクローゼットの仕切りの一部であり、クローゼットの仕切りとしての役割はもはや不要に見えた
- Chesterton's Fence の観点だけに従えば取り除いても問題なさそうだったが、実際には時間がたつうちに荷重を支える構造物になっていた
- 別の不適切な構造変更を経る中で、このスタッドが家の2階を支えることに寄与していた
- あるものが最初になぜ作られたのかだけでなく、その後どのような追加の役割を担うようになったのかも確認しなければならない
複雑なコンピュータシステムに当てはまる教訓
- 複雑なコンピュータシステムを変更するときにも、同じ問題が繰り返される
- 変更履歴を調べ、元の設計文書を読み、特定のコンポーネントがなぜそのように作られたのかを理解することは、依然として有用である
- しかし現在、そのコンポーネントがシステムの中でどのように接続され、使われているかまで確認してこそ安全である
- コンポーネントは時間の経過とともに、当初の目的とは異なる付随的な役割を担いやすい
- 安全な変更は、過去の設計意図と現在の実際の役割をあわせて確認することから始まる
1件のコメント
Hacker Newsのコメント
ようやくこれを呼ぶ名前ができた、という感じ
自分は制御システムのサポート業務を多くやっているが、物理機器を特殊なやり方で扱うPLCコードが意図せず問題を生むことは珍しくない
「ソフトウェアで電気/機械の問題を直すたびにグレムリンが生まれる」という言葉をよく繰り返すことになる
バグの根本原因や取り除きたいプログラム上の制限を見つけても、そのコードがなぜ存在するのか分かるまでは常に却下する。理由もなく入ったコードはないのだから、タイマーやオーバーライドがなぜ必要なのかを先に把握しないといけない
すでに消えた問題に対処していたコードなら幸運だが、何らかの事故を防ぐために入れたコードなのに、人員交代で本来の目的だけが失われていることも多い。文書がなければ他人の作業をすぐに巻き戻すのは非常に慎重になる
同僚を信頼するという面もある。普通は理由もなく何かをするわけではないので、コードがあるなら目的があり、最初に十分検討されたはずだと信じるべきだ。その信頼が崩れると、あらゆる意思決定が難しくなる
コードベースの外にあるOS、ファイルシステム、データベース、HTTPエンドポイント、ハードウェアなどと相互作用する理由なら、単純なAPIやライブラリ呼び出しでない限り100%コメントを付ける
他サービスのレート制限のせいで
sleepを入れたなら、誰の要件なのか、その時点で分かっている制限値、正確でないなら「推測だが動いているようだ」ということ、超過時にシステムがどう見えるかを書くファイルシステムでも済みそうな些細なことにデータベースを使っているが、実際にはその環境で必要なやり方でファイルシステムにアクセスすると、高負荷時に暴走するシステムコールでリソースが枯渇するなら、その旨をコメントする
UbuntuがLTSリリースで修正しない広く使われているライブラリのバグ回避策もコメント対象だ。「これはイマイチなのは分かっているが、理由はこうだ」というコメントを本当にたくさん書いてきた
今すぐスケーラブルに作るのは面倒で、現時点の予想では必要もないが、大規模ではうまくいかないコードを書くときもコメントを残す。自尊心を守るのに近いのかもしれないが、「ファイル全体をメモリに読むのは承知しているが、まれで予測可能なバッチ処理で、ファイルも小さい予定なので問題ない。メモリ不足ならまずここを見ろ。オンデマンド呼び出しに変えるなら書き直せ」といった具合だ
この記事が指摘しようとしているのは、それだけでは不十分だという点だ。そのコードがあるという前提の上に、さらに何が積み上がっているかも把握しなければならないからだ
「なぜこのラングが必要なのかは分からないが、消して試してみれば分かる」
もちろん触らなかったし、試しもしなかった
電気工学者と機械工学者は歴史的にソフトウェアを電気・機械システムより軽く見てきたため、EE/MEが支配的なエンジニアリング文化ではひどいコードが生まれやすい
表向きはProfessional Engineerである人たちの間でさえ、容認しがたいほど未熟なソフトウェアエンジニアリングはいまだによくある
文書のないコードはまだしも、たいていは30年前に特注されたハードウェアで、作業対象の機器の回路図すらない
似たようなことがあった
数年前に古い家を買ったのだが、前の所有者たちが1960年代からほとんどの作業を自分でやってきた
亜鉛製の雨どいがたぶん何十年も漏れていて屋根構造の一部を傷め、屋根は70年代に内側を覆うために取り付けた木製パネルに支えられていた。つまりその木製パネルが実際に荷重を受け持っていたわけだ
この家ではもっといろいろ見つかった。たとえば屋根の端で瓦の幅が足りなくなると、追加の瓦を買う代わりにセメントと割れた陶器鉢の破片で埋めてあった
今では地震や嵐に対してその役割を石膏ボードや合板に頼っている
家を骨組みだけ残して再建するのを見ると、補強材がいくつか追加されているのが分かるはずだ。壁が倒れないようにするためではなく、壁を立て直すまで直角と平面を保つためのものだ
最初は誰かがガレージドアに車をぶつけてひどくへこませたように見えるが、よく見ると屋根がドアのレールにかろうじて支えられていて、ほぼ限界寸前だ
最初は垂木の端を継ぎ足して、以前反対側も誰かがそうしていたのだから機能するだろうという感じでガレージドアだけ交換するつもりだったが、今では屋根全体をやり直すことになりそうだ
本当に心配なのは、地下室全体にわたる怪しい配線だ。比較的新しい電線、古い布被覆線、それらをつないだビニールテープが混在している。幸い、荷重を受けている電線はなさそうだ
この記事とほとんどすべてのコメントは、本当の問題を見落としているように思う。核心は テスト不足 だ
ソフトウェアは、他のあらゆる生産手段と違って、変更を現実に反映する前に実際にテストできる
良いテストがあれば、意図が何だったか、その機能に新しい用途や新しいユーザーが生まれたかどうかは関係ない。修正してテストを回せば、その修正が良いかどうかを教えてくれる
良いテストがあれば、ソフトウェア考古学、あらゆる亀裂を知っている熟練のベテラン、複雑なシステムを頭の中でモデル化する神童、包括的な要件文書、一部のユーザー集団をモルモットにする慎重なデプロイシステムは不要になる
良いテストがあれば、システムを無作為に変更し続けて、改善が出たら止めることさえできる。GoogleがAIがランキング改善を「開発した」と報告したやり方とまったく同じだ
それなのに、テスト開発者の報酬は半分以下で、テスト部門は相対的に小さく、QAは固定された制約の厳しいスケジュールに追われ、QA出身の技術的英雄はほとんどいない。派生的で反応的な仕事に見えるからだろうか
未使用のコードとそのテストを削除したのに、実はまだ使われているかもしれない
変更後にテストが失敗したが、テストが脆くて新しい状況に合わせて直したところ、実は何かが以前の動作に依存していたということもありうる
テストは素晴らしく、十分に自己完結したシステムではそれだけで足りるかもしれない。だが、より大きなシステムでは、ときに テレメトリ や段階的デプロイも必要だ
元のコードは購入リストのVATを計算するためのものだったが、次第に製品カテゴリごとのVATキャッシュを強制更新する手段になり、当初は想定していなかった文脈で呼ばれるようになるかもしれない
コメントも同じだ。元の意図と副作用は扱えても、ずっと後になってそのメソッドやクラスがどこで使われているか、実際に何をするようになったかまでは扱えない
理想的な世界では周囲の世界が変わるときにコメントも更新されるが、現実には内部コードが一緒に変わらない限り、ほとんどそうはならない
たいていテストは腐っていく。テストにも賞味期限があるように見え、やがて一部のテストが死に始める
依存関係の問題、APIへの期待の変化、セキュリティ更新、アカウントと認証情報の期限切れ、マシンのエンドポイントや状態の変化などが絡み合って、テスト結果がもはやプログラムの正しさを示さなくなる
個別に壊れたテストを1つ直すことの事業価値はたいてい非常に低く、完全に無効化したり、本来はエラーであるべきなのに「通過」するよう強制したりすることが多い
それが10年、20年と繰り返されると、すぐに「実際に信頼しているテスト」と「忙しすぎて直したり整理したりできないテスト」に分かれる
どのテストが良くてどれが悪いかは、職務や役割の変更で失われる部族知となり、ある時点で「動くと嘘をつくテスト」と「失敗が真実かどうかをもう問わないテスト」の塊そのものが偶然に荷重を負うようになる
むしろ、プログラマーがテストを書き、コードと一緒に保管し、ビルド過程で自動実行すべきだと言うほうがよい
だが、きちんとしたテスト駆動開発であっても、良い設計や良い実践を置き換えることはできないと思う。ごく単純な仕様ですらテストでは置き換えられない
f(S)が文字列を自分自身に連結して返すとしか仕様化されていないなら、fをブラックボックスとして扱う明白なテストだけでfが正しいことを検証するのは難しい。形式仕様 も重要だいくつかの点を試すことはできても、魔法のようにたった1つの誤った値が致命的になる状況なら、テストはそれを示せない
ソフトウェア考古学、熟練のベテラン、システムを頭の中でモデル化する神童、包括的要件文書、一部のユーザーをモルモットにするデプロイシステムは風刺できるかもしれないが、どれもソフトウェアが難しいからこそ生まれた対応だ。そしてソフトウェアは本当に難しい
たいていのソフトウェアチームは、自分たちの仕事の品質に直接責任を負わなければならず、組織図の向こう側へ問題を投げることはできない
もともと重要でなかったスタッドが後になって荷重を受けるようになる、というのは理解できるが、私の経験では 怠惰な設計 の兆候に見える
少なくともソフトウェアを作る場面では、装飾用のスタッドで家の一部を支えようとしていることは分かるし、より良い新しい構造を作らずにそのままそうすると決めると、後で開発チームはかなり憂鬱になる
この記事には同意するが、こういう発見を頻繁にはしないで済むと期待できる場所で働くほうが、ずっと良い
この記事の要点は、装飾用スタッドを荷重支持要素として使うな、ということではなく、あなたが来る前に誰かがそうしていた可能性を認識しろ、ということに近い
これは基本的なチェスタトンの柵の解釈よりもさらに保守的な立場で、その基本解釈ですら多くの人が過度に制約的だとして退けている
私にはこの記事が刺さる。プログラム的に言えば、「装飾用」のモールディングを取り外したら天井が頭上に崩れ落ちた、という経験を実際にしたことがある
システムが堅牢か、危険なほどスケール不能かは文脈次第だ
「営業チームが2倍に増えて市場の100%を獲得するまで、できるだけ速く顧客を売ってオンボーディングしたら?」のような思考実験はいつでもできるし、そういう条件でもデータベースを メッセージキュー のように使うのが妥当なこともある
その結果として開発チームが苦しむことになったなら、それは失敗だったということだ。保守が難しくなったか、運用地獄になったということだ
しかし、ソフトウェアの装飾用スタッドを荷重支持要素として使うからといって、必ずしもそういう結果になるわけではない。きちんとした解決策を作る数か月を節約しつつ、見えないところで幸せに役目を果たしているシステムも多い
コードベースの他の部分は絶対に不正入力を送らないので、その分岐はデッドコードで荷重を受けていない
ところがある時点でバグが入り込んで不正入力を送り、その分岐が律儀に処理して復旧する。その瞬間、その分岐は 荷重支持分岐 になる
私が見た「偶然に荷重を受ける」成果物の中でいちばん気に入っているのは、設定ミスした
sudoだったfindコマンドにパスワードなしのsudoを許可していて、-execによって root 権限で任意コード実行が簡単に可能だったのだが、製品の重要なサポートスクリプトのいくつかがそれを使うように書かれていたいわば 荷重を受ける権限昇格 だった
数年前にキッチンをリモデルした
以前のキッチンの片端には大きな梁が通っていたのだが、私たちが家を買う前のあるリモデルで、2階を増築するために追加されたものだった。キッチンを拡張するにはこれをなくす必要があった
天井を剥がしてみると、その梁は上階の壁を支えるべき位置より約2フィート右にあった
結局修正して梁を上階の壁の中へ移し、うまく収まったのだが、元の位置について尋ねると、施工業者はだいたいこんなことを言っていた
「ちゃんとやろうとする人が一人いて、気にしない人が一人いたんです。品質は結局 最も低い設定値 に合わせられてしまうんですよ」
ソフトウェアが物理システムより優れている点の一つは、意図をより明確にするための文書化を、コード内の コメントと型 で簡単にできることだ
特に Python のような動的言語では完全ではないが、大いに助けになる
荷重を受けるスタッドに対応するたとえは、本番に行くとは思っていなかったハッカソンのプロジェクトかもしれない
実際、私たちの仕事の多くは、どうにか動くところまで何かをハックして、次の仕事へ進むというものだ
保守計画では、交換可能な各部品やアセンブリがどんな「荷重」を受けるのかを把握しておく必要があるからだ
今日のシステム工学は残念ながら当初の目標から大きく逸れてしまったが、もともとの構想はそういうものだった
近年システム工学部門の力が相対的に弱まった理由の一つは、保守計画に財務が入り込んだからだ。在庫の減価償却は容赦がなく、「何を予備品として持つか」は少なくとも私の経験では、もはやシステム工学の判断であることはめったにない
結果は予想どおりだが、航空宇宙の整備要員の基準が非常に高いことが多少は相殺している。たとえば洗濯機の修理工と比べれば、かなり優秀だ
もちろん財務は、その基準さえもさらに数段下げたがっている
ただ、「装飾的」に見える上流コンポーネントが、実際には レートリミット をかけていて、それを取り除くと残りが暴走するようなシステムをどれほど頻繁に目にするか、いつも驚かされる
ユーザーがソフトウェアのバグを無自覚に利用して、それを通常業務のフローに組み込んでしまうケースを思い出す
その結果、バグを修正すると業務フローが途切れ、不満が出る
「なぜそこにあるのかは簡単に分かった。クローゼットの仕切り壁の一部だった」と言っていたが、時がたつうちに偶然荷重を受けるようになり、別の誤った構造変更によって、そのスタッドが今では家の2階を支えるのに役立っていたという
しかし、なぜそこにあるのかが簡単に分かった、というのは明らかではない。しかも、偶然荷重を受けるようになったという点にも納得できない
あなたには間違った理由に見えても、当時の人たちにはそうではなかった理由で、意図的に荷重を受けるよう にされていた可能性もかなりありそうだ
ただ、最初になぜそこにあったのかを知っているだけでは、今それが何をしているのかまでは分からない
昔一緒に働いていた物理学のポスドクが、装置の構成の上に時々こんな札を貼っていた
「触るな。隠れた危険あり」
研究室には賢い人が大勢いて、何かを見てそれを変えてよいかどうか、自分で合理的に結論づけることに慣れていた
この札は、そうした判断をあまり急がないようにという 警告 だった