上の資産 ── のプログラム、実行を制御する定義、固定長のファイル ── は、いまの開発から距離があります。
を使えばある程度は読めます。ただし限界もはっきりしている。この回は、何を任せ、何を人が押さえるかを整理します。
1. なぜ移行が議論になるのか
まず前提を確認します。技術的に動かないから移行するわけではありません。むしろ極めて安定して動いていることがほとんどです。
| 移行の動機 | 切実さ | 備考 |
|---|---|---|
| 扱える人が減っている | 最も切実 | 退職すると引き継げない |
| 変更に時間がかかる | 高い | 制度改正への対応が間に合わない |
| 運用費用 | 中 | 保守契約や設備の維持 |
| 外部システムと繋ぎにくい | 中 | 新しい仕組みと相性が悪い |
| 動作が不安定 | 低い | むしろ安定していることが多い |
1行目が本質です。時間の制約が、技術の制約より強い。「あと何年、この人に頼れるか」という問いが、移行計画の実質的な期限を決めます。
何十年も安定稼働してきたシステムを置き換えると、新システムのほうが不安定という期間が必ず来ます。移行の判断は「良くするため」ではなく、「維持できなくなる前に移すため」という位置づけで考えるほうが実態に合います。
2. LLM が効くところ
EP.01 で扱った読み解きは、この領域でも有効です。手作業で読むより桁違いに速い。
- 処理の流れの要約 — このプログラムが何をしているかの大枠
- 入出力の洗い出し — どのファイルを読み、どこに書くか
- 分岐条件の抽出 — どういう場合にどう処理が分かれるか
- 用語の対応づけ — 略された名前が何を指しているかの推測
- 現代の言語への下書き — 構造を写した実装の叩き台
特に2番目が実務で効きます。何百本もあるプログラムの入出力関係を機械的に抽出できると、依存関係の全体像が見えます。手作業では数か月かかる作業です。抽出できれば、どのプログラムから移せるか(他から呼ばれていない末端はどこか)という順序の検討にもそのまま使えます。
3. LLM が効かないところ
一方、誤読しやすく、しかも誤読に気づきにくい領域があります。ここは人が押さえる必要があります。
| 対象 | なぜ危ういか |
|---|---|
| 固定長レコードの桁位置 | 1桁ずれても動いてしまい、値だけが狂う |
| 数値の内部表現 | 符号や小数点の扱いが独特で、変換を誤ると金額が変わる |
| 文字コードの変換 | 外字や独自の割り当てがあり、機械的には解けない |
| 実行制御の定義 | 順序や条件が環境固有で、コードだけからは読めない |
| 業務上の意味 | 「区分3は特例処理」のような、書かれていない知識 |
1行目と2行目は、間違えても処理が止まらないのが最も危険な点です。エラーにならず、金額だけが微妙に違うという結果になります。しかも移行直後は気づかず、月次の締めで発覚します。
LLM の要約は流暢なので、正しく読めたように感じます。しかし固定長の桁位置や数値表現は、実データで突き合わせない限り確認できません。要約は仮説であって、検証は別途必要だと割り切ってください。
4. 実データで確かめる
対策は単純で、実際のデータで突き合わせることです。EP.03 で扱った特性化テストの考え方を、この領域にも適用します。
from dataclasses import dataclass
@dataclassclass FieldSpec: name: str start: int # 1 始まりの開始桁 length: int kind: str # "text" | "int" | "decimal2"
# LLM に読ませて得た仮説。これを実データで検証するSPEC = [ FieldSpec("order_no", 1, 10, "text"), FieldSpec("customer", 11, 8, "text"), FieldSpec("amount", 19, 11, "decimal2"), # 末尾2桁が小数部 FieldSpec("status", 30, 1, "text"),]
def parse(line: str) -> dict: out = {} for f in SPEC: raw = line[f.start - 1 : f.start - 1 + f.length] if f.kind == "text": out[f.name] = raw.strip() elif f.kind == "int": out[f.name] = int(raw) elif f.kind == "decimal2": out[f.name] = int(raw) / 100 return out
def validate(lines) -> list: """解釈が妥当かを、実データの性質から検査する。""" problems = [] for i, line in enumerate(lines, 1): if len(line.rstrip("\n")) != 30: problems.append(f"{i}行目: 長さが 30 でない") continue try: row = parse(line) except ValueError as e: problems.append(f"{i}行目: 数値として読めない ({e})") continue # 業務上ありえない値を検出する if row["amount"] < 0: problems.append(f"{i}行目: 金額がマイナス ({row['amount']})") if row["status"] not in "PDC": problems.append(f"{i}行目: 未知の状態 ({row['status']!r})") return problems桁の解釈が1つずれていると、この検査で必ず引っかかります。金額が異常に大きい、状態に読めない文字が入る、といった形で現れるためです。仮説を実データにぶつけて壊すのが、この工程の目的です。
そして最終的には、旧システムの出力と新システムの出力を突き合わせるしかありません。EP.06 で扱った突き合わせと同じ考え方です。合計金額が1円でも違えば、どこかを誤読していると判断できます。この領域では ── 結果を採用せずに新側を並行して動かす ── が特に有効です。利用者に影響を与えずに、実データでの差を毎日確認できます。
5. 自動変換ツールの位置づけ
COBOL から現代の言語へ機械的に変換するツールがあります。変換自体はできますが、期待とずれやすい点があります。
| 期待 | 実際 |
|---|---|
| 読みやすいコードになる | 元の構造がそのまま写る。読みにくさも移る |
| 移行が終わる | 動くだけ。保守できる状態にはならない |
| 人手が要らなくなる | 変換後の整理に相応の工数が要る |
| 仕様が分かる | 分からないまま別の言語になるだけ |
最下行が本質的です。読めないコードを別の言語の読めないコードに変えるだけでは、問題は解決していません。むしろ「移行済み」という扱いになって、理解しないまま先送りされる危険があります。
使いどころがあるとすれば、移行後も当面は触らないと決めた部分です。触らないなら読みやすさは要らないので、動けばよい。触る予定の部分は、理解してから書き直すという線引きが現実的です。全部を同じ方針で扱おうとすると、どちらかに無理が出ます。部分ごとに方針を変えてよいと考えると、計画が現実的になります。
6. 進め方の整理
この領域での進め方をまとめます。急がず、しかし止まらずが基本です。
- 1扱える人がいるうちに、知識を記録する — 最も時間的制約が強い
- 2LLM で全体像を掴む — 入出力関係と処理の流れ
- 3危うい領域を特定する — 桁位置、数値表現、文字コード
- 4実データで仮説を検証する — 壊れることを確認する
- 5触る部分と触らない部分を分ける — 全部を作り直さない
- 6出力の突き合わせで確認する — 1円の差も見逃さない
1番目を最優先にしてください。コードは残りますが、人の知識は残りません。「区分3は特例」のような、コードに書かれていない知識は、その人がいるうちにしか回収できません。エンジニアの引き継ぎ術 EP.05 で扱った棚卸しが、ここでは特に切実になります。
移行の動機は技術ではなく扱える人が減ること。LLM は流れの要約と入出力の抽出に効くが、桁位置・数値表現・文字コードは誤読しやすく、しかも止まらずに値だけ狂う。対策は実データで仮説を壊すことと出力の突き合わせ。自動変換は「動くコード」を作るが「読めるコード」は作らない。そして人の知識の回収を最優先にしてください。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。