reoring

全面書き直しという甘い罠 — 動いているシステムを止めずに近代化する

アーキテクチャの近代化で最も魅力的に見えて最も失敗するのが全面書き直しである。なぜ書き直しが事業を裏切るのか、段階的移行と安全網で失われた理解を取り戻すのかを、現場と経営の判断基準として論じる。

レガシーとは、誰も理由を説明できなくなったコードのことだ

アーキテクチャモダナイゼーションの相談を受けると、最初に出てくる言葉はたいてい同じだ。「このシステムは古いので、一度作り直したい」。気持ちはわかる。だが私は、その「古い」という言葉をまず疑う。

レガシーの本質は、使っている言語やフレームワークが古いことではない。なぜその仕様なのか、どの不変条件を守っているのか、その判断根拠が誰の頭にもコードにもドキュメントにも残っていない状態のことだ。私が何度も見てきたのは、スタックは新しくなったのに、誰も変更を怖がる状態が消えていないシステムだ。言語は今風になった。フレームワークも新しい。だが、なぜこの計算式なのか、この分岐は何を守っているのか、この例外処理はどの障害に由来するのかが、相変わらず誰にも説明できない。技術は新しくなったが、理解は古いままだ。

レガシーとは、誰も理由を説明できなくなったコードのことだ。

Michael Feathers の定義は鋭い。

“To me, legacy code is simply code without tests.”

Michael Feathers, Working Effectively with Legacy Code

私はこの定義を、もう一歩広げて受け取っている。テストがないとは、振る舞いの約束がコードに残っていないということだ。何を正しさとしているのかが、実行可能な形で記録されていない。だから安全に変えられない。レガシーの本質は、ここにある。

書かれた年が古いことは、それ自体では問題ではない。十年動いているコードが、業務の不変条件を正確に表現し、テストで守られ、用語が業務と一致しているなら、それは健全な資産だ。逆に、先月書いたばかりのコードでも、何を解いているのか説明できず、テストもなく、用語が現場とずれているなら、それはもうレガシーである。

つまりモダナイゼーションが取り戻すべきものは、新しい技術ではない。失われたドメイン理解と、判断の根拠だ。技術スタックの置換は、その手段の一つにすぎない。私はここを出発点に置く。

書き直しが、最も魅力的で最も危険な選択肢である理由

「作り直したい」という欲求は、エンジニアにとって自然なものだ。他人の書いた、理由のわからないコードを読み続けるより、ゼロから自分の理解で書いたほうが速い気がする。きれいになる気がする。この感覚は強力で、そして多くの場合、間違っている。

Joel Spolsky は2000年の「Things You Should Never Do, Part I」で、ゼロからの書き直しをソフトウェア企業が犯しうる最悪の戦略的誤りと書いた。

They did it by making the single worst strategic mistake that any software company can make: They decided to rewrite the code from scratch.

Joel Spolsky, “Things You Should Never Do, Part I” (2000)

彼の論点はこうだ。読みにくく見える既存コードには、長い年月をかけて埋め込まれたバグ修正が詰まっている。一つひとつの不可解な分岐は、誰かが現場で踏んだ地雷の跡だ。書き直すと、その蓄積された知識をまるごと捨てることになる。説明されていないだけで、そこには判断の歴史が詰まっている。

なぜ新しく書いた「きれいな」システムが、しばしば古いものより脆くなるのか。Fred Brooks が『The Mythical Man-Month』で述べた second-system effect が効いている。最初のシステムを慎重に作った技術者が、二番目のシステムでは抑えていた装飾や一般化をすべて盛り込もうとする。前回我慢した機能、汎用化したかった抽象、理想のアーキテクチャ。それらが一斉に流れ込み、二番目のシステムは過剰設計で膨らむ。理解しないまま作り直すと、より複雑で、より説明できないシステムが生まれる。

ここに経営の視点が加わると、罠はさらに深くなる。書き直しの間、事業は止まらない。顧客は今日も既存システムを使い、競合は新機能を出し続ける。書き直しチームが一年後に「ようやく現行と同じことができるようになりました」と報告したとき、現行システムはその一年で別物に進化している。終わらないキャッチアップが始まる。私は約60名の組織を経営しているが、書き直しの最大のコストは工数ではなく、機会の停止だと考えている。動いて稼いでいるものを止めることの重さを、技術判断だけで決めてはいけない。

書き直しが手放すのは、古いコードではない。そこに積み上がった理解そのものだ。

なぜ「数で押す」発想がモダナイゼーションを壊すか

書き直しが破綻しかけると、よくある反応は人を増やすことだ。だがレガシーの近代化において、人海戦術は効きにくい。理由は単純で、ボトルネックが手の数ではなく理解の深さにあるからだ。

問題領域を理解していない人を10人投入しても、理解されないコードが10倍速く積み上がるだけだ。これは私が一貫して持っている信条と同じ構図にある。数で押すのではなく、理解の深さで応える。モダナイゼーションでも、最初にやるべきは増員ではなく、失われた判断根拠の再発見だ。

その再発見は、たいてい泥臭い。動いているシステムの挙動を観察し、なぜこの計算式なのか、なぜこの順序なのかを、業務の言葉に翻訳していく。コードを読む前に、業務を読む。ここで取り戻したいのはコードの構造ではなく、ユビキタス言語と不変条件だ。それがないまま移行を始めれば、新しいコードもまた理由を説明できないコードになる。

ドメインモデルから、何を残し何を捨てるかを決める

線を引く順番も、スタックの新旧からではなく、ドメインモデルから入る。私の場合は、ドメインの専門家とエンジニアを同じ場に集めて、業務イベントの流れを書き出すところから始める。

最初にやるのは、システムを境界づけられたコンテキストに切り分けることだ。巨大な一枚岩を一度に近代化しようとしてはいけない。業務上意味のある単位、つまり用語と規則が一貫している範囲で線を引く。この線引きは技術的な分割ではなく、業務の構造を読み直す作業である。

線を引いたら、優先順位はコアドメインから決める。私の判断基準は、技術的な書きやすさではなく、事業の競争力を生んでいるかどうかだ。中核領域こそ、最も丁寧に理解を再建する価値がある。逆に、どこにでもある汎用的な領域や、すでに代替手段が市場にある領域は、近代化の対象ですらないことが多い。買う、置き換える、あるいはそのまま塩漬けにする。

ここで効いてくるのが、作らない判断だ。

近代化は、すべてを新しくする作業ではない。何を残し、何を捨て、何を作り直すかをドメインモデルから判断する作業である。技術的に美しいかどうかではなく、事業の選択肢を広げるかどうかで切る。私はこの優先順位付けを、近代化の最初の設計判断として扱う。コアドメインと境界をどう切るかの考え方は品質についての記事でも書いたが、モダナイゼーションの文脈では、それが「再発見」になる点が違う。すでにあるものから、失われた意図を読み直す。

絞め殺しの木 — 動かしながら置き換える

では書き直さずにどう近代化するのか。私が基本戦略にしているのは、段階的な置き換えだ。Martin Fowler が名付けた Strangler Fig Application の比喩は的確だ。絞め殺しの木は、宿主の木に巻きついて少しずつ成長し、最終的に元の木を覆い尽くす。古いシステムを一気に倒すのではなく、新しいシステムを古いものの周囲で育て、機能を少しずつ移し、気づけば古い幹が役目を終えている。Fowler はこのパターンを当初 Strangler Application と呼び、後に Strangler Fig Application と改称した。

実務では、まず外縁のリクエストを受ける層を置き、新旧のどちらに処理を流すかを切り替えられるようにする。最初の一機能だけを新しい側に移し、残りは旧システムに通す。問題が起きれば旧側に戻す。この「いつでも戻せる」状態を保てることが、書き直しに対する決定的な優位だ。事業を止めない。賭けを小さく刻む。

新旧が並走する境界では、必ず腐敗防止層を置く。Eric Evans が『ドメイン駆動設計』で示した Anti-Corruption Layer だ。新しいモデルは業務をきれいに表現しているが、旧システムは歪んだデータ構造、意味のずれた用語、古い前提を抱えている。境界に変換層を置き、旧世界の概念を新しいモデルの言葉へ翻訳する。

私は腐敗防止層を、単なる技術的なアダプタとは考えていない。これは理解を守る防壁だ。再建したばかりのドメインモデルを、旧コードの暗黙の前提から隔離する。ここを省くと、近代化したはずのコアが、半年で旧システムの言葉に引き戻される。

段階的移行は遅く見える。だが事業を止めず、いつでも引き返せる移行は、止まったまま進む書き直しより、はるかに速くゴールに近づく。安全に刻むことが、速度と品質を両立させる唯一の道だと私は考えている。

安全網のないモダナイゼーションは綱渡りだ

段階的移行が成立する前提がある。移したときに「壊れていないこと」を確認できる仕組みだ。これがなければ、機能を一つ移すたびに祈ることになる。

ここで必要なのが特性テスト、characterization test である。これも Feathers が示した手法で、レガシーコードの「あるべき振る舞い」ではなく「現にそうなっている振る舞い」を先にテストとして固定する。仕様が失われているのだから、まず現状を正しさの基準として釘で打ち込む。そのうえで移行し、同じ入力に対して同じ出力が返ることを確認する。これがあって初めて、リファクタリングや置き換えが賭けでなくなる。

私はブラウザの受け入れテスト自動化を Test as a Service として事業にしていた時期がある。そこで痛感したのは、テストは品質を上げる道具である以前に、変更を可能にする土台だということだ。テストがあるから安心して変えられる。安心して変えられるから、近代化が前に進む。テストのないモダナイゼーションは、命綱なしの綱渡りに等しい。

そしてもう一つ、本番の挙動を見続ける観測性が要る。テストは移行前に書ける範囲を守る。だが本番には、テストに書ききれなかった入力、想定外のデータ、特定顧客だけの経路がある。新旧を並走させながら、レスポンス・エラー率・遅延・差分を観測し、新側に流す割合を慎重に増やす。異常が出れば旧側に戻す。観測があるから、トラフィックを少しずつ移すという賢いやり方が取れる。

  • 特性テスト: 移行前に現状の振る舞いを固定し、置き換えの正しさを判定する基準にする
  • 観測性: 本番の差分とエラーを見ながら新側への切り替えを段階的に進め、異常時に戻せるようにする

セキュリティ出身の癖もあって、私は移行の証跡を残すことにこだわる。いつ、誰が、どの機能を新側に切り替えたか。その記録がないと、障害が起きたときに原因を切り分けられない。移行で本当に怖いのは、壊れたことではなく、壊れたことに気づけないことだ。テストと観測と証跡は、その「気づけない」を潰すための一組の装置である。

組織と運用を近代化しないと、コードだけが浮く

ドメインモデルを再建し、境界を切り、安全に移行した。それでも近代化が完了しない場合がある。組織と運用が古いままだと、新しいアーキテクチャは旧来の働き方に縛られ続ける。

コードの境界がきれいに切れない本当の理由は、たいてい技術ではない。組織だ。Melvin Conway が1968年に指摘したとおり、組織はその通信構造を写したシステムを設計する。私はKDDIのAndroid黎明期のR&Dや、日本HPのSnapfishのような大規模サービスに関わってきた。そこで繰り返し見たのは、組織図の壁がそのままシステムの結合点に固着している光景だった。部門をまたぐ承認、別チームが握る共有データベース、誰も全体を理解していない連携処理。アーキテクチャの問題に見えるものの多くは、組織の問題が物質化したものだ。

だから境界づけられたコンテキストを切り直したなら、それを担うチームの形も合わせて見直す必要がある。Skelton と Pais が『Team Topologies』で体系化した逆コンウェイ戦略は、望むアーキテクチャの境界を先に決め、それに合わせてチームの責任とコミュニケーション経路を設計するという発想だ。近代化したいなら、まず誰がどの境界を所有するのかを決める。技術選定はその後だ。

そして運用モデルそのものも近代化の対象だ。デプロイ、リリース、観測、権限、証跡、復旧。テストと観測がないコードがレガシーであるなら、再現できず証跡も残らない運用もまた、レガシーである。手順書しかない運用は、テストのないコードと同じだ。誰かが正しく実行する前提でしか成り立たず、その正しさを機械が保証していない。私は腐敗防止層を、データモデルの防御としてだけでなく運用モデルの防御としても使う。古いシステムの権限の渡し方、エラーの伝え方、リトライの前提を、新しい運用ワークフローにそのまま持ち込ませない。クラウドやKubernetesは使う。だが特定ベンダの運用モデルに閉じ込められない形で持つ。この運用を移行可能な状態へ作り変えること自体を、私はAppThrustの記事で書いた方向で進めている。

モダナイゼーションは一度きりのプロジェクトではない

モダナイゼーションは、いつか終わるプロジェクトではない。

ゴールは新しいアーキテクチャを手に入れることではない。明日も安全に変えられる状態を取り戻し、それを維持し続けることだ。理解は放っておけばまた失われる。用語はずれ、不変条件は暗黙化し、テストは形骸化する。新しいスタックも、放っておけばまたレガシーになる。だから近代化は、一度の大手術ではなく、継続的な営みとして組織に組み込む。

AIは移行を加速するが、何が正しいかは肩代わりしない

この継続的な営みに、いまAIが入り込む。レガシーの近代化は、AIが具体的な仕事をしてくれる領域だ。読解しにくい古いコードの意図を推測し、特性テストの叩き台を生成し、ある言語から別の言語への移行を下書きする。手作業なら数週間かかる解析が、桁違いに速くなる。私自身、移行の現場でAIを使う。

だが、ここで冒頭の話に戻る。AIが加速するのは移行の作業であって、何を正しさとするかの定義ではない。どの用語を業務の言葉として採用するか、どこに境界を引くか、どの不変条件を守るべきか、何を捨てるか。これらはドメイン理解の問題であり、人間が判断するしかない。ユビキタス言語、ドメインモデル、特性テスト、CI、本番の運用フィードバック。この正しさの基準が無いまま移行をAIで回せば、誰も理由を説明できないコードを、より速く、より大量に生み出すだけだ。レガシーの再生産が加速する。

だからAIに必要なのは、無制限の権限ではない。変更を提案し、検証し、差分とリスクを示し、必要なところで人間の承認を得て、証跡を残しながら安全な経路で適用するための、近代化された運用モデルだ。AI時代のモダナイゼーションは、順番がこれまで以上に重要になる。先に正しさの基準を組織が持ち、境界を決め、テストで振る舞いを固定し、観測で結果を見る。その基盤の上でこそ、AIは安全な加速装置になる。基盤がなければ、加速は崖へ向かう。

モダナイゼーションの本体は、最初から最後まで、失われた理解を取り戻すことにある。AIはその速度を上げる。だが、理解そのものは肩代わりしない。

書き直したくなったら、まず問うべきだ。我々は何を正しさとしているのか、と。その答えを持っているなら、書き直す必要すらないことが多い。