CTS-KB
モダナイゼーション📚 シリーズ 1/7

レガシー置換とは:旧実装を原典にし、先にテストへ落としてから移す

⏱ 約 11 分で読めます
#レガシー置換#モダナイゼーション#技術的負債#AI駆動開発#ハーネス#PHP#C##.NET MAUI#帳票#TDD

🎯 この記事の範囲

モダナイゼーションの失敗は、だいたい同じ形をしています。新しい画面のモックから書き始め、旧システムの「つもり」で仕様を復元し、切替の直前に現場の例外が噴き出す。

本シリーズは逆です。原典は旧実装です。PHP、2010 年前後の C# デスクトップ、iPhone / Android の MAUI。動いているコード、紙に出ている帳票、現場が毎日押しているボタンが、仕様書より先にあります。

革新は AI が読めることではありません。旧実装が今日通している入力と拒否を、先にテストへ落とすことです。この種の業務システムでは単体テストはほとんど無く、書けない開発環境が普通です。継ぎ目を切ってユニットと接続テスト(Moq / フェイク)を揃えると、実プリンタ無しで誤送を落とせます。

本記事隣接領域
本記事(第 1 回)なぜ置換するか / 原典は旧実装 / 置換の鍵は TDD / 寄せる境界 / 機械で閉じる受入と人が残る切替 / 再現にハーネスが要る理由AS/400 を残して外側に足す 戦略、TDD 編、ハーネスの作り方

営業の数字・見積・請負条件は本シリーズの対象外です。数字は www の開発実績(置換ケース) に置きます。ここは なぜ着手し、どうやれば次も同じように移せるか を書きます。

端末選定は、業務要件と運用コストに関わる設計判断として扱います。関連事例「JAN出荷検品の端末選定 — BT-W85から.NET MAUIへ」では、中古iPhone・Androidと専用ハンディを比較する条件、機器接続の分離、実機での受入を整理しています。


🎯 なぜ、置換するのか

「古い実装の技術的負債を取り除く」では、焦点がぼやけます。負債返済は結果です。現場が動き出す理由は、だいたい次の 3 つです。

動機起点本シリーズが書くこと
今は動かしていない過去実装を、別系統で再利用したいソースはある。現行業務は通っていない休眠と死んでいるを分ける。欲しい機能だけをテストに落として移す
他人が作ったシステムを、自分のプロジェクトへ入れたいリポジトリ(SVN / Git)は分かる。パスが分からない。作者はいないステアリングを組む前の調査。地図を先に出す
他社が作ったシステムに手が出せない手直しのたびに高額見積でも、代替が無い旧実装を原典に、自社側で直せる形へ移す(内製化)

3 が発注理由です。1 と 2 は、3 を安全にやるための起点です。

AS/400 モダナイゼーション が「基幹を残して外側に足す」なら、本シリーズは 手が出せない実装を、読める原典にして自社側へ移す 話です。新規の SaaS を頭の中の仕様から書く話でもありません。

どうやるかの革新は、次節からの TDD です。動機を手法に置き換えません。


🚫 新規のつもりで書くと、置換は壊れる

レガシー置換で最初に捨てるべき幻想は、「今の頭で正しい業務を再設計すれば、旧システムは消せる」です。

現場が 15 年動かしてきた例外は、仕様書にありません。納品書の見出し幅が 13mm では Windows の太字で折り返す、バーコード下の数字は均等配置、ラベルは PDF にしない、取得した印刷ジョブを排出完了としない。これらは 動いているコードと紙 にしか残っていません。

AI に「新しい出荷 SaaS を作って」と頼むと、きれいな CRUD が出ます。AI に旧 WPF と旧 RDLX を読ませ、「この紙面とこの状態機械を壊さず移せ」と頼むと、別の仕事になります。後者が本シリーズの対象です。

💡 置換の TDD とは、新しい仕様からテストを書くことではありません。 旧実装が今日通している入力と拒否を、先にテストにすることです。原典だけだと AI は欠陥まで移植します。テストだけだと、きれいな CRUD のテストに逃げます。二つ揃って、死んでいるコードを捨て、休眠は欲しい機能だけ移し、欠陥を再現せず、堅牢に置換できます。

実践編で書いた「AI は放置すると負債を増やす」は、ここでも成り立ちます。原典もテストも無い新規は増幅です。原典をテストに落とした置換は返済です。

旧実装(原典)→ テストに落とす(TDD)→ 新実装 → 実機・実紙(人)

📖 原典は仕様書ではなく、動いている実装

CTS がいま置換している対象は、だいたい次の層です。

層旧側の実体新側でやっていること
業務端末古い C# デスクトップ、MAUI(iPhone / Android)共通 MAUI。ログイン後の権限で利用可能な業務を出し分ける
Windows の無人印刷ActiveReports 依存の WPF同じ役割の WPF。PDF をスプーラーへ渡す。ActiveReports は使わない
A4 帳票RDLX + VB 式Scriban の HTML/CSS + Playwright Chromium
感熱ラベル機種の印字コマンドSBPL を .NET で組み立てる。PDF に寄せない
サーバー旧 API・採番・印刷ジョブテナント付きの契約へ移す。接続と切替は後段

PHP や 2009〜2012 年の C# は、消す対象ではなく 読み解く対象 です。先にやるのは書き直しではなく、台帳です。

📖 原典は仕様書ではなく、動いている実装

旧実装PHP / 古い C# / RDLX / MAUI

台帳

動いている

死んでいる

休眠

欠陥

TDD単体 / Moq・フェイク

新アーキテクチャへ移植

移植しない

再現せず直す

実機・実紙・契約

台帳で分けるのは、機能名ではありません。

  • 動いている — 現場が今日も使っている。紙が出る、スキャンが通る、例外処理が残っている
  • 死んでいる — ソースにはあるが、現行業務が通らない。互換のための移植はしない
  • 休眠 — 今のラインでは止まっているが、別のフロント/バックで同じ機能が欲しい。死んだ原紙の復活ではなく、後期仕様を正として載せ替える
  • 欠陥として残っている — 動いているが、バグである。共有の可変値で帳票種別が食い違う、印刷日欄が 2mm、未対応文字の黙った ? 置換

死んでいるコードを「互換のため」に移植すると、新システムが旧システムの墓地になります。休眠は墓地ではありません。今は通っていないことと、捨てることとは別軸です。欠陥は再現しません。移植時に直し、直したことを台帳に残します。

旧コードの読み方そのものは第 2 回で掘ります。総論で固定するのは原則だけです。AI に読ませるのは「全部移植しろ」ではなく、この四分類です。地図が無い状態からの調査も、第 2 回の起点です。


🧱 全部を 1 つのエンジンに寄せない

置換で二番目に捨てる幻想は、「新しい描画エンジンに全部載せれば単純になる」です。

A4 納品書は HTML/CSS の mm 指定とヘッドレス Chromium で出せます。203 dpi の感熱ラベルは出せません。既存資産は SBPL です。PDF をラベル機へ送る「統一」は、単純化ではなく誤送です。

実例は ActiveReports → Playwright + Scriban 移行 に書きました。本シリーズから見ると、あれは帳票という 一つの境界の引き方 です。

寄せる寄せない
帳票データはテナント・拠点付きの DTOテンプレートが店名から拠点を推測する
同じ MAUI で複数業務を出すログイン前から全業務メニューを出す
印刷ジョブ ID で追跡するスプーラー受理を紙の排出にする
レビュー済みテンプレートだけ実行する利用者が任意のテンプレートを投稿する
A4 は Chromium PDFラベルも PDF にする

AS/400 モダナイゼーション が「基幹を残して外側に足す」なら、本シリーズは 端末と帳票と印刷を、旧コードを原典に載せ替える 話です。Rehost / Replatform / Refactor の分類より先に、「何を一つの契約にし、何を機種の言語のままにするか」が決まります。

境界の引き方は第 3 回です。総論の結論は、統一は目的ではない。誤配線を禁止できる境界が目的です。


✅ 受入は文章ではなく、落ちるテストにする

「現行と同じに動く」は受入条件になりません。同じ、の単位が無いからです。この種の出荷・帳票などの業務システムでは、旧環境に単体テストがほぼありません。設計器と実プリンタが正本で、Moq で切る継ぎ目が無かったためです。書けなかったテストを、置換の受入そのものにします。詳細は 第 4 回 です。

機械で閉じられるものから先に閉じます。

機械で閉じる人に残す
テナント/拠点が一致しない文書は描画しない実紙のミシン目・プリンター倍率
HTML 混入はエンコードする実機スキャナでの読取
12 明細 / 7 商品で改ページする運送会社の送り状発行
Code128 / QR を生成し、画像から復号して元値と一致するドライバーからの本番自動印刷
用紙に収まらない文字は PDF を書かずに失敗する複数端末をまたぐ実運用の排他
再起動後に同じジョブを二重送信しない税・送料・値引き・適格請求書
PDF 用経路へ SBPL を流さない切替日と切戻し

帳票では、Linux 上でブラウザーを含むテストが通り、Windows の Chromium / Chrome でも同じページ数の PDF が出るところまで来ています。それは エンジン置換の受入 であって、現行紙への切替条件ではありません。

Linux では閉じないテストの出先は第 5 回、人が残る切替は第 6 回です。総論で固定するのは、手元のサンプル PDF が綺麗なことを、切替としない ことです。


🧩 なぜ、途中でハーネスが要るのか

ここまでの手順は、優秀な一人と優秀なモデルがあれば、一回はできます。一回できることと、次の置換でも同じ品質でできることは違います。

CTS-EC で直した負債が CTS-POS で再発した、という記録は ハーネスのサービス化 にあります。置換でも同じ穴が開きます。

  • 旧コードの四分類が会話の中だけにあると、次のセッションが死んだ分岐を復活させ、休眠を捨てる
  • 「PDF に寄せない」がルールファイルに無いと、ラベルを HTML にしたくなる
  • 受入テストがゲートに無いと、AI はサンプル PDF の成功を完成と宣言する
  • 期限切れの印刷ジョブを再送してよい、が文書に無いと、親切な再試行が誤出荷になる

ハーネス はモデルではありません。原典の置き場、禁止事項、テスト、証跡、周回の停止条件です。置換が AI 向きなのは原典があるからで、原典を次の実行でも同じ優先度で読ませる装置がハーネスです。

本シリーズはハーネスの作り方を再解説しません。作り方は ステアリング駆動開発 と 応用編 にあります。ここでは、置換の現場で ハーネスが無いと壊れる箇所だけを示します。それが第 7 回です。

📌 簡単に移行できる、の中身は新しいモデルではなく、台帳とゲートです。 旧実装を ZIP で渡して「新しくして」では、新規のつもりで書くのと同じ失敗を高速化します。


📍 いま閉じていること、閉じていないこと

2026 年 9 月時点の到達点です。完成宣言ではありません。

閉じつつあるもの

  • 旧 RDLX の受注票兼納品書を Scriban へ移植し、社内出荷番号の Code128 と日本語 QR をサーバー側 SVG で埋め込む
  • A4 は Playwright、ラベルは SBPL、という出力の分離
  • 印刷ジョブの claim / submitted / 要確認。スプーラー受理を排出完了にしない
  • 同じ MAUI を権限で出し分ける方針
  • WPF から ActiveReports を外す

まだ人の切替側にあるもの

  • 現行紙との重ね合わせ、ミシン目
  • 送り状 API、実機スキャナ、PW208 への実送信
  • CTS-EC 本体の API / DB への接続、複数端末排他の通し
  • iPhone / Android 実機での検品オペレーション

ソースの調査と移植とテストは AI に出せます。切替は出せません。この切り分けが、次の案件でも使い回せる定義です。


✅ 第 1 回まとめ

  • 動機は負債返済ではない。手が出せない他社製、パスが分からないリポジトリ、今は止まった過去実装の再利用である
  • レガシー置換の原典は、新しい仕様ではなく 動いている実装 である
  • 革新は AI が読めることではなく、その振る舞いを先にユニットと接続テストへ落とす TDD である
  • 旧環境はテストを書かないのではなく、書けない。新側で Moq / フェイクの継ぎ目を切る
  • 死んでいるコードは移植しない。休眠は欲しい機能だけ移す。欠陥は再現せず、直した事実を台帳に残す
  • 全部を一つのエンジンに寄せない。A4 とラベル、複数正本、送信と排出は境界にする
  • サンプル PDF の成功を切替にしない
  • 同じ置換を次でも再現するには、ハーネス が要る

次回「旧コードの読み方」では、リポジトリは分かるがパスが分からない状態から、何を先に台帳化し、何を読ませないかを書きます。


📚 シリーズ記事

回タイトル主題
1レガシー置換とは(本記事)旧実装を原典にし、先にテストへ落とす
2旧コードの読み方地図が無い状態から四分類
3寄せる境界・寄せない境界DTO、PDF と SBPL、店舗と別拠点
4受入の機械化ユニットと Moq・フェイクで先に落とす
5Linux から Mac / Windows へ出して TDD するSSH 先で閉じる不足分、キャプチャまで AI
6人が残る切替実機・実紙・契約・並行稼働
7再現装置としてのハーネス台帳・禁止・ゲート・停止条件

🔗 関連リソース

営業側の置き場(本シリーズの外)

すでに公開した置換の実例

基幹を残す話(本シリーズとは別)

再現装置

用語