5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

テーブル定義書・ER図を自動生成して、AIにテーブル構造を答えさせるツールを作ってみた

5
Last updated at Posted at 2026-10-17

はじめに

テーブル定義書・ER図を自動生成し、AIにテーブル構造を答えさせるツール「dbxray」を作成したので紹介します。

業務であまり詳しくないサブシステムのテーブル構造をキャッチアップするため、過去に作成したテーブル定義書の出力ツールを改良しました。

以前【PostgreSQL】Markdown形式のテーブル定義書を自動生成するツールを作ってみたで紹介した、テーブル定義書のみを出力するツールの改良版です。
ツールの名前は、exportTableDefinitionからdbxrayに変更しました1。

DBに接続してテーブル定義書を出力するだけでなく、出力したデータをもとにAIがテーブル構造を把握できるよう、MCPサーバー2も用意しました。
例えば、MCPサーバーを登録したClaude Codeには、次の質問ができます。

  • 従業員まわりのテーブルを探して
  • 監査ログから部署名を出したいけど、どうJOINすればいい?
  • employeeテーブルを変更したら、どこに影響する?
  • employeeと構造が似たテーブルはある?

想定読者

  • 巨大なシステムで、テーブル定義書などのドキュメントが整備されていない環境で開発している方
  • Claude Code等のAIでコードを書く際に、テーブル構造もAIに参照させたい方

リポジトリについて

dbxrayは、ebichan88/dbxrayのGitHubリポジトリで公開しています。

細かい設定や出力の仕様は、CLIリファレンスとMCPサーバーのドキュメントをご覧ください。

本記事の内容は、執筆時点の最新版であるv0.4.2に基づいています。

  • 対応DBMS
    • PostgreSQL(PostgreSQL 13以上を推奨)
    • Oracle(Oracle 12c以上を推奨)
  • 動作環境
    • Java 21
    • 配布用のzipにJava実行環境を同梱しているため、Javaのインストールは不要です。

目次

dbxrayとは

dbxrayは、DBに接続し、テーブル一覧・テーブル定義書・ER図などをMarkdown形式で出力するツールです。
あわせて、出力したデータをAIから検索できるようにするMCPサーバーも同梱しています。

出力物は次の通りです。

出力物 内容
README 出力した全ドキュメントへの索引と、スキーマごとのオブジェクト数の集計
テーブル一覧 対象テーブルの一覧と、各テーブル定義書へのリンク
テーブル定義書 カラム・インデックス・制約・外部キー・被参照・トリガーの情報など
ER図 外部キーの関係を表すMermaid記法の図。テーブル単位とスキーマ単位で出力する
観点ごとのページ 人事管理などの業務の単位でまとめた、テーブルのER図と一覧
関数・シーケンス・ユーザー定義型の一覧と個別ページ 関数・プロシージャの定義本体や、ENUMの値など
スナップショット DBから取得したスキーマの情報を、機械可読なJSON Lines形式にしたもの。MCPサーバーの入力になる
参考情報 観点など、スキーマの情報ではないがAIに渡したい情報。スナップショットとあわせてMCPサーバーの入力になる

使い方

Releasesから、OSに合ったzipをダウンロードして展開します。

展開後、conf/config.ymlに接続先DBの情報を記載します。

conf/config.yml(postgresqlの例)
database:
  driver: org.postgresql.Driver
  url: jdbc:postgresql://<IP>:<ポート番号>/<DB名>
  username: <ユーザ名>

パスワードはconf/config.ymlではなく、環境変数DBXRAY_DB_PASSWORDで渡します。
AIエージェント等から読まれる可能性のある設定ファイルに、パスワードを残さないためです。

実行(Linux/macOS)
export DBXRAY_DB_PASSWORD='パスワード'
./run.sh

Windowsの場合は、コマンドプロンプトでset DBXRAY_DB_PASSWORD=パスワードを実行した後、run.batを起動します。

処理が完了すると、outputフォルダ配下に次の構成で出力されます。

出力先の構成(抜粋)
output
├─testdb                          ・・・ Markdownのドキュメント(DBごと)
│  ├─README.md
│  ├─tableList_testdb.md
│  ├─erDiagram_testdb_sample.md
│  ├─viewpoint_testdb_personnel.md
│  └─sample
│     ├─table
│     │  └─employee.md
│     └─function
├─snapshot                        ・・・ スナップショット(MCPサーバーの入力)
│  └─testdb
│     ├─database.json
│     └─sample
│        ├─tables.jsonl
│        └─functions.jsonl
└─insights                        ・・・ 参考情報(MCPサーバーの入力)
   └─testdb
      └─viewpoints.json

出力されるドキュメント

本章では、サンプルDBから出力したドキュメントを紹介します。
出力したMarkdownは、GitHub等でそのまま閲覧できます。

出力サンプルの全体は、サンプルDBのREADMEから参照できます。

本記事の出力例・スクリーンショット・実行例は、すべてリポジトリに同梱しているサンプルDBから生成した架空の情報です。実在の個人データや業務データは含みません。

README

DBごとに、出力した全ドキュメントへの索引となるREADMEを出力します。
スキーマごとのオブジェクトの数や、論理名が記述されているテーブル・カラムの数などの集計も載せています。

READMEの出力例

テーブル一覧

対象テーブルの一覧と、各テーブル定義書へのリンクを出力します。

テーブル一覧の出力例

テーブル定義書

テーブルごとに、テーブル情報・カラム情報・インデックス・制約・外部キー・被参照・トリガーなどの情報を出力します。
次のキャプチャは、employeeテーブルの定義書です。

テーブル定義書の出力例

被参照情報には、このテーブルを参照しているテーブルを一覧にしています。
外部キーは参照する側のテーブルにしか定義されないため、参照される側から関連を辿れるようにしています。

テーブル定義書の外部キー情報・被参照情報

ER図

ER図は、GitHub等でそのまま図として表示できるMermaid記法で出力します。
次の図は、employeeテーブルの定義書に出力したER図です。

自テーブルは全カラムを、周辺のテーブルは図中の関連をつなぐカラムのみを表示します。
周辺のテーブルまで全カラムを表示すると、箱が大きくなりすぎるためです。

ER図は、次の粒度で出力します。

出力先 内容
各テーブル定義書の「ER図」セクション 自テーブルと、直接の参照先・参照元のテーブル。描画する段数は1〜3で変更できる
スキーマ別ER図 スキーマ全体のER図
グループ別ER図 スキーマが大きい場合に、つながったテーブルのまとまりごとに分割したER図
観点ごとのページ 後述する観点の、所属テーブル同士のER図

多重度の判定

関連線の多重度は、参照元テーブルの外部キーのカラムに設定された制約から機械的に判定します。
外部キー自体は関連の有無しか示さないため、カラムの一意性とNOT NULL制約をあわせて判定に使います。

外部キーのカラムが一意 外部キーの全カラムがNOT NULL 多重度 Mermaidの表記
× ○ 1対多 A ||--o{ B
× × 0..1対多 A |o--o{ B
○ ○ 1対1 A ||--o| B
○ × 0..1対1 A |o--o| B

前述のER図では、次のように判定しています。

  • employee.department_id: NOT NULLで一意ではない → 1対多
  • employee.manager_id: NULLを許容し一意ではない → 0..1対多
  • employee.parking_spot_id: UNIQUEでNULLを許容 → 0..1対1
  • employee_profile.employee_id: 主キーなので一意かつNOT NULL → 1対1

一意性は、カラム名の一致ではなく包含関係で判定します。

一意性を包含関係で判定する例
-- 外部キーは、NOT NULLの a・b の2カラム
ALTER TABLE child ADD FOREIGN KEY (a, b) REFERENCES parent (a, b);
-- a だけで一意なら、(a, b) の組も一意になるため1対1と判定する
ALTER TABLE child ADD UNIQUE (a);

逆に、UNIQUE (a, b)があっても、外部キーが(a)のみであれば1対1にはなりません。

また、一意制約に加えてCREATE UNIQUE INDEXで作成した一意索引も判定に使いますが、PostgreSQLの部分インデックスは対象外としています。

判定から外す部分インデックスの例
-- 削除されていない行の中での一意性しか保証しないため、多重度の判定には使わない
CREATE UNIQUE INDEX ON child (parent_id) WHERE deleted_at IS NULL;

整備されていないDBで使うための機能

ここまでの出力例では、外部キー制約やコメントが設定されているテーブルを中心に紹介しました。
ですが、実際の現場のDBでは、外部キー制約やコメントが整備されていなかったり、テーブル数が多すぎてER図を一度に出力しても読めなかったりすることも多いと思います。

dbxrayには、このような環境でも使えるようにするための機能を用意しています。

外部キー制約やコメントの代わりに情報を書き足す

サンプルDBでも、departmentテーブルにはコメントを、audit_logテーブルには外部キー制約を付けていません。

このようなDBに無い情報は、サイドカーYAMLに記述して出力にマージできます。

annotations.sample.yml(サンプルから抜粋)
tables:
  sample.department:
    description: |
      社内の組織単位を管理するマスタテーブル。
      従業員(employee)は必ずいずれか1つの部署に所属する。
    remarks: 組織改編時はdepartment_codeを変更せず、department_nameのみ更新すること
    columns:
      department_code: 部署コード。他システム連携時のキーとして使用
      department_name: 部署名称

relations:
  - table: sample.audit_log            # 参照元(子)
    columns: [record_id]
    parentTable: sample.employee       # 参照先(親)
    parentColumns: [employee_id]
    cardinality: 0..1対多              # DBに制約が無いので、多重度はここで明示する

tablesに記述した説明・備考は、テーブル定義書のテーブル説明や備考の欄に反映されます。

サイドカーYAMLの内容を反映したdepartmentのテーブル定義書

relationsに記述した関連は、論理リレーションとして扱います。
テーブル定義書では外部キー情報とは別のセクションに載せ、ER図では物理的な外部キーと区別するため破線で描画します。
前述のemployeeのER図で、audit_logとの線が破線になっているのが論理リレーションです。

audit_logのテーブル定義書の論理リレーション情報

どこまで情報を補えたかは、READMEの集計で確認できます。

大きなスキーマのER図を分割する

テーブル数が多いスキーマでは、全体を1枚のER図に描くと線が入り乱れて読めなくなります。
そこで、1枚に描くテーブル数に上限を設け、上限を超えた場合は次のように出し分けます。

小さなまとまりを同じグループにまとめるのは、2テーブルだけの小さなER図が大量に生成されるのを避けるためです。
まとめたテーブル同士は線でつながらないため、関連を誤読するおそれはありません。

業務の単位でテーブルをまとめる

スキーマ単位やつながり単位のER図は機械的な区切りのため、業務の単位でテーブルを把握したい場合には向きません。

そこで、人事管理・受注管理のような業務の単位でテーブルをまとめる観点を、サイドカーYAMLで宣言できるようにしています。

annotations.sample.yml(サンプルから抜粋)
viewpoints:
  - id: personnel
    name: 人事管理
    description: |
      従業員と、その所属・付帯情報を扱うテーブル群。
      プロジェクトへのアサインは「プロジェクト管理」の観点を参照。
    tables:
      - sample.employee*
      - "!sample.employee_directory_view"
      - sample.department
      - sample.parking_spot

観点を宣言すると、観点ごとにER図と所属テーブルの一覧をまとめたページを出力します。

観点「人事管理」のページ

ドキュメントの更新漏れを検知する

ドキュメントを出力しても、DBの変更に追従できなければすぐに古くなってしまいます。

--checkを付けて実行すると、コミット済みのスナップショットと、現在のDBから生成したスナップショットを比較します。
マイグレーション後のドキュメントの再出力漏れを、CIで検知するための機能です。

例えば、サンプルDBのemployeeテーブルにnicknameカラムを追加してから実行すると、次のように差分をunified diff形式で表示します。

--checkの出力(一部抜粋)
Content differs:
 - table sample.employee
--- committed/testdb/sample/tables.jsonl (table sample.employee)
+++ generated/testdb/sample/tables.jsonl (table sample.employee)
@@ -15,6 +15,7 @@
     {"name":"profile","logicalName":"プロフィール(JSON)","type":"jsonb","primaryKey":false,"notNull":true,"defaultValue":"'{}'::jsonb"}
     {"name":"hired_date","logicalName":"入社日","type":"date","primaryKey":false,"notNull":true,"defaultValue":"CURRENT_DATE"}
     {"name":"updated_at","logicalName":"更新日時","type":"timestamp without time zone","primaryKey":false,"notNull":true,"defaultValue":"now()"}
+    {"name":"nickname","logicalName":"ニックネーム","type":"text","primaryKey":false,"notNull":false}
   ]
   "indexes": [
     {"name":"employee_employee_code_key","method":"btree","unique":true,"primary":false,"definition":"CREATE UNIQUE INDEX employee_employee_code_key ON sample.employee USING btree (employee_code)"}

差分がある場合はCIのジョブを失敗させられるため、ドキュメントの更新漏れに気づけます。

MCPサーバーでAIにテーブル構造を答えさせる

本章では、MCPサーバーについて紹介します。

当初はMCPサーバーを作る予定はありませんでしたが、開発の途中でふとテーブルの構造をAIが回答できたら面白いのではと思い、AIに提案したところ思いのほか話が進みました。
現在は、19個のツールを提供するMCPサーバーになっています。

全体像

全体の構成は次の通りです。

ポイントは、MCPサーバーがDBに接続しない点です。
dbxrayが出力したスナップショットと参考情報を読み込むだけなので、AIの実行環境にDBの接続情報を置く必要がなく、テーブルの実データがAIに渡ることもありません。

また、人が読むテーブル定義書とAIが参照する情報は、同じ取得結果から出力しているため、両者に食い違いは生じません。

一方で、DBを変更しても、dbxrayを再実行してスナップショットを更新するまで、変更内容はAIから参照できません3。

設定方法

MCPサーバーの本体は、配布用zipに含まれるmcp/dbxray-mcp.jarです。
同梱のJava実行環境で動作するため、Claude Codeの場合は次のコマンドで登録できます。

Claude Codeへの登録(Linux/macOSの例)
claude mcp add dbxray -- /opt/dbxray-linux/runtime/bin/java -jar /opt/dbxray-linux/mcp/dbxray-mcp.jar --snapshot=/work/schema-docs/output/snapshot

MCPサーバーは、--snapshotで指定したディレクトリと同じ階層のinsightsから参考情報を読み込みます。
snapshotだけを共有すると観点や関数の利用テーブルを参照できないため、snapshotとinsightsの両方をコミットしてください。

また、上記に限らず、業務DBの出力には内部情報が含まれうるため、アクセス制御されたリポジトリで管理する等、公開範囲には十分に注意してください。

チームで共有する場合は、アプリのリポジトリに.mcp.jsonをコミットし、環境ごとに異なるパスは環境変数で指定します。

.mcp.json
{
  "mcpServers": {
    "dbxray": {
      "command": "${DBXRAY_HOME}/runtime/bin/java",
      "args": ["-jar", "${DBXRAY_HOME}/mcp/dbxray-mcp.jar", "--snapshot=${SCHEMA_SNAPSHOT_DIR}"]
    }
  }
}

AIに質問できること

ここでは、サンプルDBに対してツールを実行した結果を交えながら、AIに質問できる内容を紹介します。

業務の言葉でテーブルを探す

テーブル名が英語でも、論理名・説明・カラム名を対象に日本語で検索できます。
次は、search_tablesで「従業員」を検索した結果です。

search_tables(query: 従業員)の結果(一部抜粋)
{
  "total": 3,
  "tables": [
    {
      "name": "employee",
      "logicalName": "従業員",
      "matchedIn": ["logicalName", "column:employee_id", "column:employee_code", "column:employee_name", "column:manager_id"]
    },
    {
      "name": "employee_history",
      "logicalName": "従業員履歴",
      "matchedIn": ["logicalName"]
    },
    {
      "name": "department",
      "description": "社内の組織単位を管理するマスタテーブル。\n従業員(employee)は必ずいずれか1つの部署に所属する。\n",
      "matchedIn": ["description"]
    }
  ]
}

departmentテーブルにはDBのコメントがありませんが、サイドカーYAMLで記述した説明に「従業員」が含まれているため、検索結果に含まれています。
人が補った情報も検索対象になるため、情報を補うほどAIがテーブルを探しやすくなります。

カラム名からテーブルを逆引きすることもできます。

「物理名にidが存在するカラムがどれだけあるか調べてほしい」と聞いた結果

JOINの経路を調べる

監査ログのaudit_logから、変更された従業員の部署名を取得したいケースを考えます。

audit_logには外部キー制約がありませんが、サイドカーYAMLでemployeeへの論理リレーションを宣言しているため、find_join_pathでJOINの経路を辿れます。

find_join_path(from: audit_log、to: department)の結果(一部抜粋)
{
  "paths": [
    {
      "tables": ["sample.audit_log", "sample.employee", "sample.department"],
      "joins": [
        {
          "from": "sample.audit_log",
          "fromColumns": ["record_id"],
          "to": "sample.employee",
          "toColumns": ["employee_id"],
          "kind": "logicalRelation",
          "cardinality": "OPTIONAL_ONE_TO_MANY"
        },
        {
          "from": "sample.employee",
          "fromColumns": ["department_id"],
          "to": "sample.department",
          "toColumns": ["department_id"],
          "kind": "foreignKey",
          "cardinality": "ONE_TO_MANY"
        }
      ]
    }
  ]
}

結果には、JOINするカラムの組に加えて、外部キーと論理リレーションのどちらによる関連かと、多重度も含まれます。
この結果をもとにSQLを組み立てると、次のようになります。

返ってきた経路から組み立てたSQLの例
SELECT a.changed_at, a.action, e.employee_name, d.department_name
FROM sample.audit_log a
-- record_idはNULLを許容する0..1対多の関連なので、外部結合にしておく
LEFT JOIN sample.employee e ON e.employee_id = a.record_id
LEFT JOIN sample.department d ON d.department_id = e.department_id
-- record_idには他のテーブルの主キー値も入るため、employeeの変更に絞る
WHERE a.table_name = 'employee';

外部キー制約の無いDBでは、AIはカラム名から関連を推測してJOINを書くことになりますが、dbxrayを使えば人が宣言した関連をもとに回答できます。

変更の影響範囲を調べる

get_impactを使うと、テーブルを変更・削除した場合の影響範囲を1回の呼び出しで調べられます。
employeeテーブルの調査結果をまとめると、次の通りです。

種類 影響を受けるもの
外部キーで参照しているテーブル attendance、employeeの自己参照、employee_history、employee_profile、project_assignment
論理リレーションで参照しているテーブル audit_log
間接的に参照しているテーブル attendance_note。attendanceを経由する
参照しているビュー employee_directory_viewと、それを参照するattendance_monthly_view
トリガー trg_employee_audit、trg_employee_set_updated_at
使っている関数・プロシージャ raise_salary、close_project、department_headcountなど4つと、候補のcount_rows

関数・プロシージャについては、対象テーブルを登録・参照・更新・削除のどの操作で使っているかを、operationsにC・R・U・Dで返します。
また、ビューのトリガーから呼び出されてemployeeに登録する関数のように、トリガー経由の利用もcalledByTriggersで確認できます。

get_impact(table: employee)の結果のうち関数の部分(一部抜粋)
"usages": [
  {
    "function": "sample.raise_salary",
    "kind": "PROCEDURE",
    "overloads": ["IN p_employee_id integer, IN p_amount numeric"],
    "operations": "U"
  },
  {
    "function": "sample.employee_directory_insert",
    "kind": "FUNCTION",
    "overloads": ["()"],
    "operations": "C",
    "calledByTriggers": ["sample.employee_directory_view.trg_employee_directory_insert"]
  }
]

候補としたcount_rowsは、スキーマ修飾の無いemployeeを参照しているため、実行時のsearch_pathによっては別のテーブルを指す可能性があります。
このような関数は確定扱いにせず、match: possibleを付けて区別します。

関数の利用テーブルは、関数の定義本体から機械的に抽出したもので、--previewを付けて実行した場合のみ有効になるプレビュー機能です4。

構造の似たテーブルを探す

中でも個人的に気に入っているのが、構造の似たテーブルを探すfind_similar_tablesです。
スナップショットのデータをそのまま返すのではなく、類似度の算出などの工夫を加えています。

「employeeと構造が似たテーブルはある?」と聞いた結果

このとき、MCPサーバーはAIに次のJSONを返しています。

find_similar_tables(table: employee)の結果
{
  "base": "sample.employee",
  "total": 1,
  "tables": [
    {
      "database": "testdb",
      "name": "sample.employee_history",
      "logicalName": "従業員履歴",
      "similarity": 77,
      "kind": "superset",
      "sharedColumns": 11,
      "onlyInOther": ["history_id", "valid_from", "valid_to"],
      "typeMismatches": [
        {
          "column": "employee_name",
          "base": "character varying(50)",
          "other": "character varying(100)",
          "difference": "size"
        }
      ],
      "relatedToBase": true
    }
  ]
}

employeeの11カラムをすべて持ち、history_id・valid_from・valid_toが追加されたテーブルとして、履歴テーブルのemployee_historyを検出しています。
さらに、employee_nameの桁数のみが本体と異なることも分かります。

新しいテーブルを作る前に同じ目的のテーブルが既に存在しないかを確認したり、履歴・バックアップ用のテーブルを見つけたりする用途を想定しています。

ER図を取得する

テーブルの周辺や観点単位のER図を、テーブル定義書と同じ表記のMermaid記法で返します。

「従業員テーブルの周辺のER図を描いて画像で見せて」と聞いた結果

構造の似たテーブルを探す仕組み

find_similar_tablesは、カラム名の集合同士を重み付きJaccard係数で比較します。
比較の前に、カラム名の大文字小文字・全角半角を統一します。

通常のJaccard係数は、共通するカラムの数を、どちらかのテーブルにあるカラムの数で割って求めます。

\text{Jaccard係数} = \frac{\text{共通のカラムの数}}{\text{両者のカラムの和集合の数}}

重み付きJaccard係数では、カラムの数の代わりに、カラムごとの重みの合計で計算します。

\text{類似度} = \frac{\sum_{c \in \text{共通のカラム}} w(c)}{\sum_{c \in \text{両者のカラムの和集合}} w(c)}
,\qquad
w(c) = \ln \frac{N + 1}{\text{そのカラム名を持つテーブルの数}}

ここで、$N$ はDBのテーブル数です。
$N$ とそのカラム名を持つテーブルの数は、どちらもビューと基準のテーブル自身を除いて数えます。

重み $w(c)$ は、そのカラム名を持つテーブルが多いほど小さくなります。
これは、idやcreated_atのような多くのテーブルに共通する汎用的なカラムを共有しているだけで、類似と判定しないためです。

SimilarTableFinder.java(一部抜粋)
    double weightOf(String name) {
      return Math.log((tables + 1.0) / Math.max(counts.getOrDefault(name, 0), 1));
    }

結果には、類似度に加えて次の情報も含めています。

  • kind: 相手との関係の種類
    • identicalはカラムが同じ、supersetは相手が基準の全カラムを持つ、subsetは相手が基準の一部だけを持つ、overlapはそれ以外
  • typeMismatches: 同名で型が違うカラム
    • 型そのものが違うのか、桁数だけが違うのかも区別する
  • relatedToBase: 基準と外部キー・論理リレーションで直接つながるテーブルかどうか

基準とつながるテーブルを除外せず印を付けるのは、履歴テーブルや拡張テーブルが基準のテーブルを参照していることが多いためです。

なお、類似度はカラム名の構造の近さを示すもので、同じ意味のテーブルとは限りません。
同じ目的のテーブルかどうかは、呼び出したAIがget_tableで説明や定義を確認して判断する役割分担にしています5。

ツールの一覧

v0.4.2時点で、次の19個のツールを提供しています。

目的 ツール 内容
全体像をつかむ list_schemas DB・スキーマと、種類ごとのオブジェクトの数
list_viewpoints 観点の一覧
list_table_clusters 関連のつながりから推測した、テーブルのまとまり
探す search_tables キーワードでテーブルを検索する。論理名・説明・カラム名も対象
list_tables テーブルの一覧と、それぞれの関連の数
find_columns カラム名から、そのカラムを持つテーブルを逆引きする
find_similar_tables カラム名の構造が似たテーブルを探す
定義を見る get_table テーブルとビューの定義
関連をたどる get_related_tables 外部キー・論理リレーションでつながるテーブル
find_join_path 2つのテーブルをつなぐ最短のJOIN経路
get_er_diagram Mermaid記法のER図
影響を調べる get_impact テーブルを変更・削除したときの影響範囲
その他 list_functions・get_functionほか 関数・シーケンス・ユーザー定義型・トリガーの一覧と定義

ツールのほかに、利用者が@でテーブルの定義を会話に添付できるリソースも提供しています。
ターミナル版のClaude Codeでは、入力欄に@受注明細のように論理名・物理名を入力すると候補が表示されます。

開発で苦労したこと

今回の改修の大部分はClaude Codeで実装しましたが、最も苦労したのはAIが作成したソースコードの理解です。

元々は自分が1から作ったツールのため、ソースコードは熟知していたはずですが、気づけば、実装の進み方に対して自分の理解が追いつかなくなる瞬間がありました。

そこで、可読性・責務分割・命名・例外設計・DDDとしての構成などを一つずつ見直しながらリファクタリングを進め、最後にソースコードの読解ガイドも整備しました。

読解ガイドは、事前知識・処理の流れ・ソースごとの役割と関係を図でまとめたもので、GitHub Pagesで公開しています。

また、読解ガイド以外にも、AIが実装したコードの品質を保つために次の仕組みを導入しています。

  • Spotlessによるコードフォーマットの検査
  • JaCoCoによるカバレッジの最低基準の検査
  • ArchUnitによるレイヤー構成・コーディング規約の検査
  • SpotBugs・CodeQLによる静的解析
  • Docker上のPostgreSQL・Oracleに対する結合テストと、出力全体のベースラインとの比較
  • MCPサーバーのツール仕様と実装の一致を確かめる契約テスト
  • DB由来の文字列によるパストラバーサル・HTML注入を想定したテスト
  • AIエージェント向けのルールをまとめたAGENTS.md・コーディングガイドライン

各仕組みの詳細は、リポジトリのCONTRIBUTING.mdとコーディングガイドラインをご覧ください。

おわりに

機能強化を進める中で、このツールのコンセプトが現行システムの読み解きであると明確に言語化できたと感じました。

巨大なシステムで、テーブル定義書などのドキュメントが整備されていない環境で開発している方には、ぜひお勧めします。

  1. 旧名はテーブル定義書に特化した名前だったため、より汎用的な名前にしたいと考えていました。当初はGitLensにならって「dblens」とする予定でしたが、同名のリポジトリが既に存在しているようだったため、重複しない名前にしました。 ↩

  2. MCP(Model Context Protocol)は、AIが外部のツールやデータを使うための標準規格です。詳細は公式サイトをご覧ください。 ↩

  3. この鮮度の問題には、前述の--checkをCIで実行し、スナップショットとDBの乖離を検知することで対応する想定です。 ↩

  4. 動的SQLや、呼び出し先の関数内での参照は含みません。こうした取りこぼしの可能性がある関数は、結果の中で別途示します。 ↩

  5. DBのテーブル数が少ない場合、汎用的なカラムの重みが十分に下がらないという制約もあります。 ↩

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?