【生成AI✕コードマイグレーション】COBOLをLLMでJavaに変換する検証レポート

【生成AI✕コードマイグレーション】COBOLをLLMでJavaに変換する検証レポート

Summary generated by AI

  • COBOLシステムの保守担当者不足と、「2025年の崖」問題が深刻化している。既存の変換ツールでは、保守性の低い「Jobol(Java + COBOL)」コードが生成されやすい。
  • COBOLコードを直接変換せず、一度LLMで「設計書」を生成し、それを元にJavaコードを実装する「設計書経由アプローチ」を検証した。
  • タスク管理機能を持つCOBOLコード(TODOAPP)を使用。STEP1で仕様を抽出し、STEP2でSpring Bootを用いたJavaコードへ変換した。
  • AIはCOBOLコードが「単一タスクしか保持できない」制約を持っていることを見抜き、Java化に際して「複数タスク管理(リスト構造)」への改善を提案・実装した。
  • 手続き型コードからの脱却、ドキュメントの復元、および仕様不備(バグ)の発見と修正案の提示が可能となる。
  • AIが意図せず仕様を変更してしまうリスクがあるため、生成された設計書とコードに対する人間によるレビューが必須である。

はじめに

DX(デジタルトランスフォーメーション)推進の波が押し寄せる中、多くの企業で足かせとなっているのが「レガシーシステム」の存在です。 特に1960年代から稼働し続ける「COBOL」で記述されたシステムは、長年の改修により複雑化し、ブラックボックス化しています。

さらに、経済産業省が「2025年の崖」という語を用いて警鐘を鳴らしている通り、COBOLエンジニアの高齢化と退職による「人材不足」は深刻です。 この状況下で、最新のLLM(大規模言語モデル)を活用し、これら技術的負債を解消できないかという議論が活発化しています。

本レポートでは、これを検証対象とします。

特に、単なるコードの置き換えではなく、「COBOLコード」から一度システムの「設計書」を生成し、それを元に「Javaコード」を実装するという、設計工程を経由したマイグレーション手法の有効性を検証します。

COBOLからJavaへのマイグレーションが求められる背景と課題

なぜ今、多くの企業がCOBOLからJavaへの移行を急ぐのでしょうか。 その最大の理由は「保守性の維持」と「ビジネスアジリティの確保」にあります。 しかし、従来の手法には大きな課題がありました。

既存ツールの限界と「Jobol」の誕生

これまでも、COBOLをJavaに自動変換するツール(トランスレーター)は存在しました。 しかし、これらは構文を機械的に置き換えることに主眼を置いています。 その結果、Javaであるにもかかわらず、中身はCOBOLのロジックそのままであるため、オブジェクト指向の恩恵を受けられない「Jobol(Java + COBOL)」と呼ばれる奇怪なコードが生成されがちでした。 これでは、言語が変わっただけで、保守性の低さは変わりません。

仕様書の形骸化と紛失

また、多くの現場では「現行コードと仕様書が一致していない」、あるいは「仕様書そのものが紛失している」という問題があります。 正しい仕様がわからないため、人間が手作業でJavaへ書き直す(リライト)際も、現行コードを一行ずつ解読する必要があり、膨大なコストと時間がかかっていました。

生成AIを活用したコード変換のアプローチ:設計書を経由する理由

本検証では、以下のプロセスを採用します。

  1. COBOLLLM(リバースエンジニアリング)設計書

  2. 設計書LLM(実装)Java

なぜ直接変換しないのか

COBOLから直接Javaへ変換させることもLLMは可能です。しかし、あえて「設計書」を経由させるには、以下の明確な「3つの理由」があります。

  1. アーキテクチャの刷新: 手続き型言語であるCOBOLのロジックを一度「要件」として抽象化することで、Javaに最適な「オブジェクト指向」や「MVCモデル」への再設計が容易になります。

  2. ドキュメント資産の復活: 形骸化した仕様書を最新の状態へアップデートし、資産として残すことができます。

  3. ハルシネーションの抑制: AIが生成したコードが正しいか検証するのは大規模システムにおいてはかなりの労力を必要としますが、生成された「日本語の設計書」があれば、業務有識者が内容の妥当性をチェックしやすくなります。

なお、本検証ではLLMとして、論理推論能力とコーディング能力に定評がある「Gemini 3 Pro Preview」を使用します。

検証対象:COBOLで記述されたTODOアプリのサンプルコード

検証には、タスク管理を行う簡易的なTODOアプリのCOBOLコードを使用します。 このコードは、ユーザーからの入力を受け付け、タスクを追加・表示する簡易機能を持ちます。

検証用COBOLコード(抜粋)

以下は、検証に使用するTODOAPP.cblです。データ部(DATA DIVISION)で変数を定義し、手続き部(PROCEDURE DIVISION)で処理を行っています。

IDENTIFICATION DIVISION.
PROGRAM-ID. TODOAPP.

DATA DIVISION.
WORKING-STORAGE SECTION.
01  TASK-RECORD.
    05  TASK-ID       PIC 9(04).
    05  TASK-NAME     PIC X(20).
    05  TASK-STATUS   PIC X(10).
01  WS-EOF            PIC A(1) VALUE SPACES.
01  COMMAND-INPUT     PIC X(1) VALUE SPACES.

PROCEDURE DIVISION.
MAIN-PROCEDURE.
    DISPLAY "--- TODO LIST APP ---".
    
    PERFORM UNTIL WS-EOF = 'Y'
        DISPLAY "ENTER COMMAND (A:ADD, L:LIST, Q:QUIT): " WITH NO ADVANCING
        ACCEPT COMMAND-INPUT

        EVALUATE FUNCTION UPPER-CASE(COMMAND-INPUT)
            WHEN 'A'
                PERFORM ADD-TASK
            WHEN 'L'
                PERFORM LIST-TASKS
            WHEN 'Q'
                MOVE 'Y' TO WS-EOF
            WHEN OTHER
                DISPLAY "INVALID COMMAND"
        END-EVALUATE
    END-PERFORM.
    
    DISPLAY "BYE!".
    STOP RUN.


ADD-TASK.
    DISPLAY "  [INFO] ADD-TASK FUNCTION CALLED.".
    DISPLAY "  Enter Task ID (4 digits): " WITH NO ADVANCING
    ACCEPT TASK-ID
    DISPLAY "  Enter Task Name (up to 20 chars): " WITH NO ADVANCING
    ACCEPT TASK-NAME
    MOVE "Pending" TO TASK-STATUS
    DISPLAY "  Task '" TASK-NAME "' (ID: " TASK-ID ") added with status '" TASK-STATUS "'.".
    EXIT.

LIST-TASKS.
    DISPLAY "  [INFO] LIST-TASKS FUNCTION CALLED.".
    IF TASK-ID IS NUMERIC AND TASK-ID NOT = ZEROES THEN
        DISPLAY "  ID: " TASK-ID ", Name: " TASK-NAME ", Status: " TASK-STATUS "."
    ELSE
        DISPLAY "  No tasks added yet."
    END-IF.
    EXIT.

なお、このコードには、レガシーシステムによく見られる特徴があります。 それは、TASK-RECORDという単一のメモリ領域を使い回しているため、厳密には「最新の1件しか記憶できない」 という仕様(あるいは制約)になっている点です。 AIがこの挙動をどう解釈し、マイグレーションに反映させるかが検証のポイントとなります。

【STEP1】LLMによるリバースエンジニアリング:COBOLから設計書を生成

まず、COBOLコードをLLMに入力し、仕様書を生成させます。

ここでは、プロンプトの工夫として、単に「解説して」と指示するのではなく出力形式を明確に指定しました。

プロンプト例: 「以下のCOBOLコードを解析し、機能要件、データ項目、および処理フローをMarkdown形式で出力してください。現状のコードの制約事項も指摘した上で、Webアプリ化する際のあるべき仕様を提案として記載してください。」

生成された設計書(成果物)

LLMはコードの論理構造を正確に読み解き、以下の設計書を出力しました。

1. 機能要件定義

  • タスク登録 (ADD-TASK):

    • ユーザーから「タスクID(4桁)」と「タスク名(20文字)」の入力を受け付ける。

    • ステータスは自動的に初期値「Pending」が設定される。

  • タスク参照 (LIST-TASKS):

    • 現在保持しているタスク情報を表示する。

    • 未登録時(IDがゼロまたは非数値)は「No tasks added yet.」を表示する。

2. 現状コードの分析とWeb化への提言 LLMは、コード上の重要な制約を鋭く指摘しました。

現状の制約: TASK-RECORD変数が単一の領域として定義されているため、タスクを追加入力すると以前のデータが上書きされます。リスト管理機能としては不完全です。

Webアプリ化への提言: LIST-TASKSという機能名やアプリケーションの目的から、本来は「複数のタスクを管理する」ことが意図されていると推測されます。Java移行時には、データベースまたはリスト構造を用いて、複数のタスクを永続化・一覧表示できるように改修することを推奨します。

このように、LLMは単なる翻訳だけでなく、「コードからプログラマの意図を推論する」 という高度な分析を行いました。これにより、「仕様のバグ」をそのまま移植するリスクを回避できます。

【STEP2】生成AIによる実装:設計書を基にしたJavaコードへの変換

次に、STEP1で生成された設計書(およびAIからの改善提案)を入力として、Javaコードを生成させます。

プロンプトの工夫

Javaのフレームワークとして「Spring Boot」を指定し、RESTful APIとしての実装を求めます。また、AIの提言を採用し、複数タスクを管理できる仕様へのアップグレードを指示します。

プロンプト例: 「以下の設計書と改善提案に基づき、Spring Bootを用いたTODO管理アプリケーションを作成してください。H2 Database(インメモリDB)を使用して複数のタスクを保存・取得できるようにしてください。{{ここに設計書を記載}}」

生成されたJavaコード

出力されたコードは、COBOLの制約を脱し、モダンなWebアプリケーションに近づきました。

1. Entityクラス(データ構造) 入力項目のIDとName、自動設定されるStatusが定義されています。

package com.example.todo;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.Data;

@Entity
@Data
public class Task {
    @Id
    private Long id; // COBOL仕様に合わせて自動採番(@GeneratedValue)せず、手動入力を許可
    private String name;
    private String status;
}

2. Serviceクラス(業務ロジック) COBOLのADD-TASK内にあった「StatusにPendingをセットする」というロジックが、適切にService層へ移管されています。

package com.example.todo;

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class TaskService {
    private final TaskRepository repository;

    public TaskService(TaskRepository repository) {
        this.repository = repository;
    }

    public Task createTask(Long id, String name) {
        Task task = new Task();
        task.setId(id);
        task.setName(name);
        task.setStatus("Pending"); // ビジネスロジック
        return repository.save(task);
    }

    public List<Task> getAllTasks() {
        return repository.findAll();
    }
}

3. Controllerクラス COBOLのEVALUATEによるコマンド分岐は、HTTPメソッド(POST, GET)によるルーティングに変換されました。

package com.example.todo;

import lombok.Data;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/tasks")
public class TaskController {
    private final TaskService taskService;
    
    public TaskController(TaskService taskService) {
        this.taskService = taskService;
    }

    @PostMapping
    public ResponseEntity<Task> addTask(@RequestBody TaskRequest request) {
        Task created = taskService.createTask(request.getId(), request.getName());
        return ResponseEntity.ok(created);
    }

    @GetMapping
    public List<Task> listTasks() {
        return taskService.getAllTasks();
    }
}

// リクエスト受け取り用のDTO
@Data
class TaskRequest {
    private Long id;
    private String name;
}

4. Repositoryクラス

package com.example.todo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TaskRepository extends JpaRepository<Task, Long> {
}

5. アプリケーションのエントリポイント

package com.example.todo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TodoApplication {
    public static void main(String[] args) {
        SpringApplication.run(TodoApplication.class, args);
    }
}

変換結果の検証:Javaコードの動作確認と品質評価

動作検証

生成されたコードを実行し、APIクライアントからアクセスして検証を行いました。

  1. タスク追加(POST):

    • ID: 1001, Name: Design Review を送信。

    • 応答: {"id": 1001, "name": "Design Review", "status": "Pending"}

    • 結果: ステータス「Pending」の自動付与ロジックが正しく動作しています。

  2. タスク追加(POST - 2件目):

    • ID: 1002, Name: Implement API を送信。

    • 結果: エラーなく保存されました。COBOL版では上書きされてしまう箇所ですが、Java版では別レコードとして保存されています。

  3. 一覧取得(GET):

    • 結果: 10011002の両方のタスクを含むJSON配列が返却されました。AIの提案による「リスト管理化」が成功しています。

コード品質評価

  • ロジックの継承と改善: MOVE "Pending" TO TASK-STATUS というビジネスロジックは維持しつつ、データ構造は最新化されています。

  • 可読性: WS-EOF のような制御フラグを使ったループ処理が消え、ステートレスなREST API構造になったことで、可読性が劇的に向上しました。

AI駆動開発によるマイグレーションのメリットと実用上の注意点

今回の検証を通して、LLM活用によるマイグレーションの現実解が見えてきました。

メリット

  1. 「Jobol」化の回避: 設計書を挟むことで、COBOLの手続き的な呪縛(PERFORM, GOTOなど)から解き放たれ、Javaらしいオブジェクト指向コードが生成されました。

  2. 潜在的な仕様不備の発見: 今回の「上書きされてしまう問題」のように、AIがコードから「本来あるべき姿」を推論・指摘してくれるため、移行と同時に品質改善が期待できます。

  3. ドキュメントの再整備: 最新のJavaコードとセットで、Markdown形式の仕様書が手に入ります。

実用上の注意点・リスク

一方で、実務適用には注意が必要です。

  1. 「勝手な補完」のリスク: 今回はAIが気を利かせて「リスト構造」にしましたが、もし「常に最新の1件だけ表示する」ことが正しい業務仕様だった場合、これは仕様改変になります。AIの提案を受け入れるか否かは、必ず人間が判断する必要があります。

  2. COBOL固有機能の再現: REDEFINES句によるメモリ共有や、10進演算の精度など、Javaで直感的に再現しにくい機能については、厳密な単体テストが必要です。

  3. セキュリティ: 業務コードをLLMに入力する際は、学習データとして利用されないセキュアなAPI環境(Azure OpenAI Service等)の利用が必須です。

まとめ

検証の結果、「COBOL → 設計書 → Java」というプロセスは、レガシーマイグレーションにおいて有効であることが確認されました。

特に、COBOLコード内のロジック(ステータスの初期値など)を正確に抽出しつつ、アーキテクチャ(単一変数→リスト構造)を刷新できた点は大きな成果です。AIは単なるトランスレーターではなく、優秀な「設計補佐」として機能します。

もちろん、最終的な仕様の正当性判断は人間が行う必要がありますが、エンジニアは「解読」という重労働から解放され、「判断・レビュー」というコア業務に集中できるようになります。このAI駆動のアプローチこそが、「2025年の崖」を乗り越える鍵となるでしょう。

FAQ generated by AI

はい、可能ですが一度ですべてを完結させることはできません。機能やモジュールごとに分割してLLMに処理させる必要があります。また、共通部品(Copy句)の依存関係整理が重要になります。

あります。今回の検証のように「リスト構造にすべき」と提案してくれる場合もあれば、誤った解釈をする場合もあります。そのため、「設計書生成」のフェーズで人間が内容をレビューし、仕様を確定させることが重要です。

コンシューマー向けの無料版ChatGPTなどに入力するのはリスクがあります。企業向けプラン(ChatGPT EnterpriseやAzure OpenAI Service、AWS Bedrockなど)を利用し、入力データが学習に使われない設定(オプトアウト)を確認した上で利用してください。

AIネイティブ実態調査レポート 無料配布中