📝

AI時代に15年運用のレガシーPHPと向き合う - 2万関数の型情報の可視化

に公開

はじめに - AI開発時代の新たな課題

CursorやWindsurf、ClaudeCode を使った開発が当たり前になり実装スピードが飛躍的に向上した中で、新たな課題が見えてきました。

「AIが間違える原因は、実は人間と同じ」

先日、AI支援でコードを書いていたときのこと。返り値がnullのはずの関数が、AIによって空文字列""を返すように変更されてしまい、後続の処理で予期しない不具合が発生しました。

// 元のコード(意図)
function getUserName($id) {
    // 基本は文字列が返る
    if (正常系) {
        return $this->name ;  // 文字列
    }

    // ユーザーが見つからない場合はnullを返したい
    return null; 
}

// AIが修正したコード
function getUserName($id) {

    // 基本は文字列が返る
    if (isset($this->displayName)) {
        return $this->displayName ;  // 文字列
    }

    if (正常系) {
        return $this->name ;  // 文字列
    }

    // ユーザーが見つからない場合に空文字列を返す(と合わせてくれた)
    return ""; 
}

// 後続処理で問題発生
$name = getUserName($id);
if ($name === null) {
    // この分岐に入らず、バグが発生
    showNoUserError();
}

このとき気づいたのは、型情報やアノテーションのないコードは、AIにとって曖昧すぎるということでした。人間だって同じです。コードの意図が明確でなければ、間違いを犯しやすくなります。

AI時代だからこそ、コードの「意図」を明確に表現することが重要になってきています。

現実と向き合う - 2008年から運用するシステムの実態

私たちが運用しているのは、2008年から続くランサーズの基盤システムです。15年以上の歴史を持つこのシステムには、多くの開発者が関わり、様々な時代の開発手法が混在しています。

AI支援開発を安全に導入するために、まず「現状を知る」ことから始めました。

課題: 型宣言やアノテーションがない関数がどれくらい存在するのか?

手作業で確認するには規模が大きすぎます。そこで、Pythonスクリプトを作成して自動解析することにしました。

ツール作成記 - AIに助けられながら

「AI開発の安全性を高めるためのツール」を「AIに助けられながら」作ることになりました。
この手のツール作成に必要なコストは圧倒的に下がったのはここ1年の大きな進歩です。ほんの10分程度で作れたのでその過程を共有できればと思います

要件定義

ここは自ら書き下したところ

  • PHPファイルを再帰的に検索
  • 関数定義を正規表現で抽出
  • ドキュメントコメント(@param, @return)の有無を確認
  • PHP 7以降の型宣言の有無を確認
  • 大規模なコードベースでも高速に処理

実装のポイント

正規表現でPHPの関数定義を正確に検出するのは複雑ですが、ここは完全にvibe cording です。AIの力を借りました(ソースコードは1行も書いてません)

# 関数定義を検出するための正規表現
# public/private/protected/static などの修飾子にも対応
function_pattern = re.compile(
    r'(?:public|private|protected|static|\s)*\s+function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{]*))?'
)

大規模なコードベースを処理するため、並列処理も導入:

# 並列処理で解析を高速化
with ProcessPoolExecutor() as executor:
    results = list(tqdm(executor.map(analyze_file, php_files), total=total_files))

AIが細かな実装の大変さを省略してくれたおかげで、本質的な課題解決に集中できました。

結果 - 2万関数の半分以上が型情報なし

解析結果:
アノテーションあり: 9,000 関数 (45.00%)
アノテーションなし: 11,000 関数 (55.00%)
合計: 20,000 関数

わかりやすく丸めてますが 55%の関数に型情報がないという現実が、数値として明確になりました。

詳細な内訳

  • PHP 7以降の型宣言のみ: 30%
  • ドキュメントコメントのみ: 10%
  • 両方あり: 5%
  • どちらもなし: 55%

レガシーシステムの典型的な状況

この結果は、多くのレガシーPHPプロジェクトに共通する状況だと思います:

  1. 時代の変遷: PHP 5時代のコードが大量に残存
  2. 開発者の入れ替わり: 当時の意図が失われている
  3. 機能追加優先: リファクタリングの時間が取れない

分析をしてみて

この数値化によって、いくつかの重要なことが見えてきました

「なんとなく型情報が足りない」という感覚的な問題が、「55%の関数で型情報が不足」という具体的な課題として明確になりました。
そして、これまでの解析は静的解析のツールやプラグインなどを使わないとコストが大きかったですが、圧倒的にコスト低く実行できるようになったのは明確に今までとの違いだなと感じています

今後への展望 - 現状把握から始める改善

現在は「現状把握フェーズ」ですが、次のような段階的なアプローチを考えています:

Phase 1: 現状把握(完了)

  • ツールによる全体の可視化
  • 問題の規模の定量化

Phase 2: 段階的改善(次のステップ)

  • 影響度の高いファイルから優先的に型情報を追加
  • 新規開発では型情報を必須とするルール策定

Phase 3: AI開発との統合

  • 型情報のある関数から優先的にAI支援を活用
  • 安全なAI開発のワークフロー確立

おわりに - 同じ課題を抱える開発者の皆さんへ

まずは把握したうえでここから型情報の充足とモニタリングを指定校と考えていますが
15年運用のレガシーシステムで直面したこの課題は、多くの開発現場に共通するものだと思います。

AI時代だからこそ、コードの品質がより重要になっています。

今回作成したツールは、GitHubで公開しています。同じような課題を抱えている方の参考になれば幸いです。

GitHub: function_mix_count

主な機能

  • PHPファイルの再帰的検索
  • 関数のアノテーション有無の判定
  • 並列処理による高速解析
  • 詳細な統計情報の出力
  • 結果のJSON出力
# 使用例
python analyze_annotations.py /path/to/your/php/project --exclude vendor tests

皆さんのプロジェクトでも、まずは「現状を知る」ことから始めてみませんか?

数値化された現実と向き合うことで、AI時代に対応した品質の高いコード基盤を築いていけると信じています。


この記事が役に立った方は、いいね👍やコメント💬をいただけると嬉しいです!

Discussion