これは経験則なのですが、ある程度を超えたスパゲッティコードはリファクタリングを試みても状況が改善しなくなります。
— なぎせ ゆうき (@nagise) 2023年6月27日
ブラックホールから光が脱出できない事象の地平線になぞらえ、これをソフトウェア品質における事象の地平線と言い(ません
リファクタリングの事象の地平線という比喩について解説をしておきます。
前提知識として リファクタリングの価値の考察 - プログラマーの脳みそ での議論を参照していただけるとありがたいです。
事象の地平線とは
事象の地平線というのは天文学用語でシュバルツシルト面などとも呼ばれます。超大雑把にいえば、ブラックホールのまわりのここから中に入ると光でさえも出ることはできないぞ、というラインです。
つまり、比喩としてはこの中に落ち込むと二度と脱出できないぞ、ということです。リファクタリングの力ではどうやってもソフトウェアの保守性を上げられない状態に陥って脱出できないという主張です。
リファクタリングのおさらい
リファクタリングの原義について再確認しておきましょう。バイブルとされるマーチン・ファウラー著「リファクタリング」では次のように書かれています。
リファクタリングとは、ソフトウェアの外部的振る舞いを保ったままで、内部の構造を改善していく作業を指します。
原義としてリファクタリングは外部的な振る舞い、つまるところ挙動を変えてはいけません。ソフトウェアというものは、プログラムというものは、同じような挙動をするものであっても様々な書き方ができるものです。その多数の選択肢の中から、いまのコードとは別の選択肢に切り替えるという捉え方もできるでしょう。より現在の状況に即したコードに切り替える。
単にコードを書き直すことは「リライト」です。外的振舞いを保って初めて「リファクタリング」と言える。そうした「リライト」と「リファクタリング」を本稿では区別します。
リファクタリングをする動機は、単にコードが稚拙だったからとは限りません。ソフトウェアの機能拡張をしたいとなったとき、今まではなんら問題なかったソースコードが、突如、都合の悪いものになってしまうことがあります。そもそも機能拡張というのは今まではなかった話ですから、その存在しなかった拡張に備えた都合の良い設計になっていないことが往々にしてある。もし未来が予め見えていたなら、より都合の良い選択肢を取ることができたのに。そうした後悔を取り返すことがリファクタリングの動機のひとつとしてあります。
リファクタリングと自動テスト
書籍リファクタリングでは第4章にて自動テストについて解説がされています。JUnitについて取り上げられていますが、しかし、書籍リファクタリングではJUnitのテストコードを前提にリファクタリングの作業が書かれているわけではありません。
これは多分に時代背景によるもので、JUnitによる自動テストがまだ十分な市民権を得てはいなかったことが挙げられるでしょう。しかし、現代にリファクタリングをやるのであれば、JUnitなど自動テストツールによる補助を使わない手はない。およそ「必須」のツールと認識されているのではないでしょうか。
大きなステップ
なぜリファクタリングによって保守性を上げられなくなるのか。その主張の中心にあるのは「大きなステップ」です。
そもそもリファクタリングは「小さなステップ」で行います。書籍リファクタリングはIDEによる機械的なリファクタリングのサポートのない時代の想定で書かれており(原書は1999年。第14章にわずかばかりリファクタリングツールについて記載があります)手動でリファクタリングを行うためのかなり細かい手順が記載されています。
本来、リファクタリングというのは小さなステップで動作確認を行いながら慎重に進めるものなのです。そうでないと「外部的振る舞いを保」てないからです。現在の状況から、外部的振る舞いを保った最寄りの飛び石に飛び乗るように進めていきます。
データ構造や、クラス構造といったものや、根底のアーキテクチャのようなところに手を加えようとすると、外部的振る舞いを保った最寄りの飛び石が、とてもジャンプして届くような位置に存在しなくなることがあります。「ここに手を加えるには、参照箇所1000箇所を一斉に変更するしかないな」ということが生じます。こうした状況を「小さなステップ」に対して「大きなステップ」「ビッグステップ」などと表現しました。
この「大きなステップ」が人間の能力の限界を超えたとき、それはもはやリファクタリング不能な、改善不能なソースコードであると言えましょう。これが脱出できない事象の地平線とたとえられる代物です。
※この「大きなステップ」「ビッグステップ」という表現は、どこかで見たような気がしていて、私のオリジナルではないつもりなのですが、この語義での利用は出典を探してもうまく見つからないのです。情報があれば教えていただけると幸いです。
リファクタリングのコスト論
リファクタリングのコストとその採算性については リファクタリングの価値の考察 - プログラマーの脳みそ にて詳しく議論しています。端的には、リファクタリングは品質特性の「保守性(maintainability)」に作用し、保守性の価値は、そのビジネス規模に依存するというものです。
一般的に、リファクタリングするべきか、しないべきか、という議論はこうしたコスト論に基づいた判断の話をしているかと思います。本稿の主張はそうではない。
「大きなステップ」がある限界値を超えると、本質的にリファクタリングそのものが不可能になるのだ、という主張です。
判別困難
リファクタリング不可能なソースコードが存在するというのが本稿の論旨なのですが、しかしおそらくソースコードを表面的に見たときに、このソースコードはリファクタリング不可能な状態にあるかどうかは判別困難でしょう。
なので、事前の予測としてリファクタリングにこのぐらいのコストがかかりそうだ(≒労力がかかりそうだ)と見積もって、先に述べたような採算を考慮してGoサインが出ることがあります。しかし忘れないでください。リファクタリング不可能なソースコードというものが存在しうるということを。
もし、事前の予想に反して、リファクタリングが困難であるならば、進捗が思わしくないならば、このことを思い出してください。そして、目の前の「大きなステップ」があまりにも大きいのであれば、諦めて撤退することを考えてください。損切りすることを選択肢として考えてください。もう少し粘ればいける、などと考えると被害は広がるばかりです。顧客からは噓つき呼ばわりされることすらあります。
大きなステップを乗り越えるもの
大きなステップは、その大きさによっては人間には克服不可能なものであると主張しました。もしこれを乗り越える可能性があるとすれば、それは機械によるパワーでしょう。
例えば、大量に参照されている、そして同名の別の変数も存在するような変数のリネームなんてリファクタリングは、およそ昔は不可能でした。しかし、現代のIDEなどの機械補助は、こうした人間には困難なリネームのリファクタリングをいともたやすく行ってくれます。
膨大な量を、漏れなく、正確に処理をするという、およそ人間には不得意な作業ですが、機械がそれを可能にしてくれるかもしれません。現代ではデータ構造や、クラス構造といったものや、根底のアーキテクチャのようなところに手を加えようとするならば、それぞれの箇所で個別に人間の判断を要するため、機械的な一括処理は困難ですが、そうした判断をも未来のAIはうまく処理してくれるかもしれません。