🍻

LLMに開発を任せる時こそ、DDDのユビキタス言語を作ろう

に公開

この記事で言いたいこと

どんなプロジェクトでも、ユビキタス言語を作ろう!

生成AIと「言語」モデル

いわゆる生成AIが加速度的に進化していく流れに乗って、ソフトウェア開発の進め方も大きく変わりつつあります。
1年前の生成AIは補助的なサポート過ぎないケースがほとんどでした。しかし、今では人間は指示を出すだけ、コーディングは全て生成AIに任せる。といったケースが急激に増えています。

さて、この生成AIと呼ばれている存在は「AI」というからにはさぞかし論理的な思考が得意だろうと思ってしまいがちですが、全くそんなことはありません。
というのも、生成AIとしてよく上げられるGPTやClaude,Geminiなどで使われるLLM(Large Language Model、大規模言語モデル)と呼ばれる技術が論理的な思考を得意としないためです。
この技術は論理な思考に直結するものではなく、その名の通り「Language」つまり「言語」を扱うAI技術になります。

「言語」というのは非常に曖昧な存在です。例えば「行けたら行く」と言ったときに文字通りに取れば「行く」可能性が高いように受け取れます。しかし、実際にはこの発言が出たら「行かない」可能性がそれなりに高いでしょう。もし発言をしたのが関西の人であれば、ほぼ確実に「行かない」でしょう。

このような曖昧な「言語」を扱う以上、LLMとの意思疎通の難易度はとても高く、多くの場合に不整合が生じます。ですので、少しでもLLMと人の間での不整合を減らすために、共通の言語を定めることが重要となります。

その為のツールとしてユビキタス言語が有用だと考えています。

ユビキタス言語とは

ユビキタス言語は、エリック・エヴァンスが提唱したDDD(ドメイン駆動設計)という設計手法の中核概念として生まれました。DDDは「ビジネスの複雑さをソフトウェアで表現するための設計手法」ですが、ユビキタス言語自体はシンプルで実用的な概念のため、DDDを採用していないプロジェクトでも十分に活用できます。
実際、多くの成功しているプロジェクトでは、DDDという名前を使わずとも「チーム内での用語統一」や「共通言語の策定」という形で、ユビキタス言語と同様の取り組みを行っています。

ユビキタス言語はプロジェクトに関わる全ての人(業務の担当者から開発者まで全て)が共通して使う「用語集」のようなものです。ただし、ユビキタス言語は普通の用語集ではなくて「生きた用語集」です。プロジェクトの進行に応じて追加や修正を頻繁に行い、普段の会話からコードまで幅広く活用する共通言語が「ユビキタス言語」なのです。

ユビキタス言語の例

例えば、このお菓子を販売するシステムを開発することを想定しましょう。

特に何も決めずに開発を開始した場合、以下のようになることが想像できます。

  • コード
// 開発者Aのコード
class ImagawayakiService {
    calculateImagawayakiPrice() { /* */ }
}
// 開発者Bのコード  
class ObanyakiController {
    getObanyakiList() { /* */ }
}
  • DBのテーブル名

    • kaitenyaki_sales テーブル
    • oyaki_stock テーブル
  • API仕様

    • GET /api/gozasoro

このようにプロジェクト内の認識が合っていないと、同じものに対して複数の名称が生まれて大混乱する可能性があります。開発中に大量のバグが出ますし、保守性が最悪なのも言うまでもありません。

ここで、ユビキタス言語を導入していればどうなるでしょうか?

ユビキタス言語.md
## ベイクドモチョチョ
小麦粉生地で餡を包み、専用鉄板で焼き上げた和菓子

- 英語表記: Baked Mochocho
- システム識別子: baked_mochocho
- カテゴリ: 焼き菓子 > 餡入り円盤型
- 別名: 今川焼き/大判焼き/回転焼き/おやき等
  • コード
// 開発者Aのコード
class BakedMochochoService {
    calculateBakedMochochoPrice() { /* */ }
}
// 開発者Bのコード  
class BakedMochochoController {
    getBakedMochochoList() { /* */ }
}
  • DBのテーブル名

    • baked_mochocho_sales テーブル
    • baked_mochocho_stock テーブル
  • API仕様

    • GET /api/baked_mochocho

このように、ユビキタス言語を定めることで、プロジェクト内で統一された用語が使えるようになり、バグも減少し、保守も楽になり、みんなハッピーになります。

ここで重要なポイントとして「ユビキタス言語は世間で通用する言葉であるは必要はない」ということがあります。つまりユビキタス言語は以下のような特徴を持つ言語なのです。

  • 内部統一が最優先
    • チーム・組織内での共通理解が最大の目的
  • ドメイン特化
    • プロジェクトが扱う領域での一意性が重要(領域外のことは気にしなくて良い)
  • 外部認知は二の次
    • プロジェクト内で統一できれば、一般的でない用語でも問題なし
  • 進化可能
    • プロジェクトの進行に応じて、ユビキタス言語も変化・発展させる

ユビキタス言語を作らずにLLMにコードを任せて失敗した話

ここで、LLMに開発をさせていたときの失敗談を書きます。

MCP Serverってあるじゃないですか。MCP Server1つに対して複数のToolを持った作りになっているのは、使ったことがある方なら知っていると思います。

公式でもToolという名称で呼んでいます。
https://modelcontextprotocol.io/docs/concepts/tools

これをアプリに組み込むときに、何を思ったのか「MCP Serverってまだエンジニア以外には伝わりにくいだろうし、画面上でツールと呼ぼう」という判断でLLMに開発させてしまいました。

この時、明文化されてない以下の概念が生まれたわけですね。

  • MCP Server
  • Tool
  • ツール (=MCP Serverのこと)

結果はお分かりですね。LLMはToolツールの区別がつかなくなり全力でバグりました。
「英語と日本語だから区別つくだろ」とか「文脈で分かるだろ」というのは個人の勝手な思いで、他人(LLM)から見れば、そんなの判断できないのですよね。

さらに悪いことに、LLMは物覚えが悪いです。というか何も覚えられません。
これはLLMのモデルの特性上モデルが作成された後に追加で学習することができないからです。
つまり一度理解してもらっても毎日同じことを説明しないとまた同じバグをだします。

この失敗をした際に、LLMとの会話に無駄に大量の時間とコストを溶かした末に、最終的に大幅な修正と用語の統一をすることになりました。

ユビキタス言語はLLMに作らせよう

タイトルに「ユビキタス言語を作ろう」と書きましたが、LLMにコードを書かせることを前提とするなら、LLM自身に作らせたほうが良いです。

これは、前述の通りLLMは何も覚えられないことが大きな理由です。
LLMは何も覚えられないのでユビキタス言語の内容を毎回理解させる必要があります。LLM自身に書かせれば、翌日のLLMも同じ内容をすんなり理解してくれる可能性が高いです。しかし人間が書いて毎回欠かさず彼らに理解してもらうには、かなりの文章能力が必要です。
自分で書いてLLMに理解させようとしたときに特に障害となるのが、LLMは理解してなくても「理解した」と言う事です。私はこの発言に騙されて、ひたすらLLMとすれ違ったやり取りを繰り返していたことがあります。
よほど文才に自信がある場合を除き、LLMにユビキタス言語を書かせて人間が理解したほうが、早くて正確な開発ができます。

また、ユビキタス言語をLLMに作ってもらうのは非常に簡単です。ユビキタス言語というものがDDDの中で明確に定義されているからです。「このプロジェクトのユビキタス言語集を作成して」と依頼するだけで、それなりに精度の高いユビキタス言語集を作ってくれます。
フォーマットや内容にこだわりがある場合は、より細かい指定をするとよいでしょう。

ユビキタス言語は頻繁に更新しよう

最初に書いた通りユビキタス言語は生きた用語集です。頻繁に更新して損することはありません。
プルリクを出す前にかならずユビキタス言語の見直しをするとよいです。また、長期間の開発になるとユビキタス言語集じたいが煩雑になるので、定期的にリファクタをすることも必要です。こういった対応もLLMに見直しを依頼するだけで高確率でいい感じに対応してくれます。常に最新で分かりやすい状態を維持しましょう。

まとめ

  • チームで開発をするときはユビキタス言語を作ろう
  • LLMと開発をするときもユビキタス言語を作ろう
  • ユビキタス言語は、人間が作るよりも、LLMに作らせよう
NCDCエンジニアブログ

Discussion