Strangler Fig Pattern とは
"ストラングラー・フィグ・パターン”
しめ殺しの木と称される「イチジク(fig)」をご存知でしょうか?
古い先行者の幹に取り付きいつの間にか全体を覆い尽くして取り付いた樹木を枯らしてしまうイチジクの生態にちなんだシステム更改の方法です。
チェルノブイリ原子力発電所第4号炉を覆い尽くす新しい石棺の話は、まさにこの「古いシステムを覆い隠して処理する」ための新しいシステムの象徴といえます。
私が取り組んだ "Strangler Fig Pattern" の事例紹介
去年からあるPHPのレガシーシステムにおいて、"SFP"を使って入れ替える作業を現在進行させています それはPHP7.2+FirebirdSQL2.5 で構築されていて、 今から10年ほど前にシステムの保守者は、「古いDB運用+PHPの問題」 から逃れることができなくなっていました。 システム制約に縛られてどうにもならない状況に陥ったようです。
この春に最新PHP+SQLite3に置き換えるという作業がほぼ完了したので記事にしました。
過去の失敗
私が知る限り、過去の挑戦者は、"Strangler Fig Pattern" によらない、システムの再構築方法としては、 "Big Bang" を企図していたようです。 具体的にはプロジェクト内部に app フォルダを設けて中に Laravel で動くサブプロジェクトを作っていました。過去の挑戦者にとっての最大の問題は何かといえばこの作業を行うコストは「動いているシステムを保守しつつ」行う必要があったことと、当時はコードを解析する作業をほぼ人力で行わなければならなかったことです。恐るべきことにドキュメントは一切されていませんでした。
ドキュメントされていない理由
私はこのシステムのリファクタリングを引き受ける時にこのように言いました。
- 全てはドキュメントありきです
- 読み取れる全てのことをドキュメントしていきます
これに対してシステム運営者はこう言うのです。
「ドキュメントをエンドユーザにまで公開すると奪われる」
モラルというか「要件定義」すらできないエンドユーザのリテラシーレベルの低さからくるこの問題は闇としか言えないと思います。
解析能力とビッグバンの罠
この「アンドキュメンテッドジャングル」に立ち向かうのに最も必要なのはなんだったのか? それは強力な機械的解析の能力でした。かつての挑戦者は全て「システムを読み解く」ために膨大な人的時間を費やしましたが、それは今や生成AIが持つ高速な構文解析と意味分析で取って代わられています。
そのような「高度な解析」を用いずとも「静的解析」「ランタイムエラーの根絶」を繰り返し行うことでも、システムの延命はできたはずです。
しかし過去の挑戦者たちはビッグバンを狙いました。彼らはよく整備された入植地しか知らなかったのもあり、彼らの手腕は整えられた世界秩序でしか発揮できませんでした。このような時、ビッグバン手法は大変なリスクを伴います。何故なら、「ある小さな関数の問いと答え」を吟味することと、彼らが知る洗練された出来合いのシステムでの「言い回し」は翻訳する以前の問題だったからです。
困ったことに、本番システムと彼らの開発環境の相違は大きな障害になったと考えられます。 大変な工数を必要とするのに人力が足りない時代です。これでは頓挫するのは自明でした。
生成AIの登場
2024年末ごろから私個人は生成AIを本格的に取り入れることを始めています。 そして去年の夏までにはgithub copilot でのコード補完からコード生成の能力向上の恩恵を享受できる状況になっていました。
過去に作った小さなシステムを新しく書き換えるのには大変役に立つ環境で、さらに「コードを読み取って分析をする」作業に関して、これまで他者が書いていて意図を把握するまでただ眺めているだけのところから、抱えている問題を解決するところまで一気に進められる能力を得たことが大きいです。
ストラングラー・フィグパターンを使えるようになる
過去にビッグバン手法を使わざる得なかった理由は「PHPコードの深読みをするよりは、原始的なBDDによるリファクタ(=振る舞いを細かく読み取って再現する)が確実だったからでしょう。 確かに挙動だけ合わせればコードの深読みは不要になるところがありますが、逆にいえば「要件定義がないからBDDを起こさざる得ない」と言う状況にあります。 しかしBDDの手法すら知らなければ、それはもはや「それらしい別物を作ってダメ出しを繰り返す」地獄になります。
ストラングラー・フィグの良いところは、「過去の資産を急いで解体することはしない」ところにあります。 巨大なコードの堆積物のロジックは膨大で、矛盾すら含みながら「人的な作業プロセスが固まってしまっている」と外しにくいところがあります。捨てられる部分のコストを回収するには「なるべく生かしながら少しずつ削る」しかありません。
逆に悪いところは「過去の資産をきちんと精算するプロセス(=コードを読んで継ぐ)作業が必ずあることです。解析能力の充実と、補助するコードの生成が速やかに行えるなら、負の遺産といえども解体できないものではなくなります。
実際に使われた手法の例
-
DB処理をシム(工業用語:隙間を埋める薄板)と呼ぶヘルパー関数に統合する作業が行われました。 具体的には、”ibase_()”を定義することでかつてFirebird2.5を念頭に書かれている多くのスクリプトでのSQL処理がSQLite3の同名スキーマに対して(概ね)適用され、本番システムであればこのまま動かせなくもないところになりました。
-
各種のヘルパー関数を生成しかつてコピペで塗りたくられて冗長・繰り返される記述を圧縮しました。3000行を超えていたコードが 700行程度に圧縮されます(ヘルパー関数が600行増えたので、ほぼ半減しました)
結果的にどうだったのか?
- 👍 ビッグバン的な手法では、作業の結果動かないスクリプトばかりになってしまうことはありませんが実際には二階建ての問題があって工数的に厳しかったと考えられます。ストラングラーフィグでは、「終わってない部分は、エラーが出ても動いているふりをしてくれる場合もある」と言う心理的なガードレールがあり、徐々に進められる利点が大きかった。
- 👎 いつまで経っても古いコードが生き延びてしまうため、生成AIモデルが「そのコードを生かさないといけない」誤認をすることが多かった。これを阻止するためのブロック記述が増えることになった
- コード解析のためコストが高く github copilotの定額の枠が足りなくなってしまうことが多かった。 人力で対応することもあったが、ハーネスコントロールに注力して安定化するまでに約1ヶ月ほど足踏みした。
まとめ
リファクタリングに終わりが見えない時はこの手法に限ります。
- この手法を意識する前は、可能な限りKGIに直行しようとするモデルによる現実乖離したコード生成に陥りがちで、非効率だったが、ハーネスコントロールの観点からは、生成AIモデルにとって報酬ポイントを小さく稼げる短工期のKPIを設ける手法は、Strangler Fig Patternとマッチしていることが再確認できた。
- 明瞭な仕様書が存在しないシステムでは、手当たり次第に実装の入れ替えをせざるを得ないため、この手法で延々と作業しなくてならない辛さは変わらないが、エンドユーザの要望に沿えるかが微妙な現場で、一旦転ぶと相当に痛いビッグバンよりは心理的抵抗が少なく、現実的だったと言える。