実装者を信じてはいけない
Algomatic の菊池氏は、AI 生成コードの品質問題に限らず、実装者の能力や注意力に依存しない「コードベースそのもの」で品質を担保する設計思想の重要性を説いている。
キーポイント
実装者への依存からの脱却
AI によるコード生成に限らず、人間による開発でも「気をつけて守るべき手順」に依存するのはリスクであり、実装者の能力や注意力に左右されない仕組みが必要である。
業務操作の抽象化とカプセル化
注文作成や監査ログ記録などの一連の処理を「placeOrder」といった単一の業務操作として名前付け、内部処理が外部から直接呼べないように設計する。
不完全な状態の防止
手順の一部を忘れるとデータ整合性が崩れるリスク(例:注文は存在するが監査ログがない)を防ぐため、ロジックを統合された関数に閉じ込める。
実装者の負担を減らす設計
保存処理に監査ログが必要というルールは、呼び出し側が毎回意識する必要がないようシステム側に強制すべきです。
型安全性による不正防止
業務上ありえない値の組み合わせを許容する柔軟な型定義ではなく、ファクトリ関数や型システムによって不正な組み合わせが作れないように設計します。
AI時代における「信用できない実装者」への対応
AIも人間も関係なく、実装を信頼できない場合こそ「間違えようがない設計」が重要となる。
AIの特性を活かした徹底された設計
AIは人間よりも面倒なルールや厳密な設計原則を疲れずに、文句も言わずに徹底的に実装できる。
影響分析・編集コメントを表示
影響分析
この記事は、生成 AI が普及した現在において、開発者が陥りやすい「AI に任せきりにする」あるいは「人間の注意力に頼る」という思考の限界を指摘し、ソフトウェア工学の根本的な原則(カプセル化と不変性の確保)への回帰を促しています。現場における技術負債やバグの削減には、ツールや AI の性能向上よりも、堅牢なアーキテクチャ設計が重要であることを示唆する内容です。
編集コメント
AI によるコード生成が一般的になる中で、逆に「人間や AI のミスを防ぐ仕組み」の重要性を再認識させる良質な技術論です。
こんにちは、Algomatic の菊池です。
Algomatic 初夏のアドベントカレンダー8日目、6本目の投稿となります。(土日はお休みです)
前日分は小笠原さんによる「ハーネスエンジニアリングを、コンサル/PMO の業務に翻訳する」です。生成 AI における概念を人間活動のメタファーで捉えることで見えてくる視点、おもしろいですよね。
さて、今日の記事では、あまり AI 関係ない話をします。実装の話です。
AI の書いた筋の悪いコードに辟易としていませんか。コードレビューで何度同じ指摘を繰り返しているのか...と嘆いている方も多いと思います。でもこれ、AI 固有の問題ではありません。
人間によるチーム開発であっても「コードの質」は常々問題になります。
実装前の設計レビューをする、コードレビューによる指摘で実装者の成長を促す、などなど色々な手段はありますが、わたしが最も重要かつ効果が高いと考えるのは「実装者の能力に依存しないコードベース」です。
この記事では、「実装者の能力に依存せず、筋の悪いコードを書けないようにする」ためのいくつかの考え方と例を示します。
実装者の注意力に依存してはいけない
あなたのソースコード、「A したら B もするようにしましょう」といった「気をつけて守らないといけない手順」ありませんか。
たとえば、ある業務操作(ここでは「注文」という行為とします)を実行するときに、データの作成、保存、監査ログの記録が必要だとしましょう。
こんなコードに見覚えありませんか。
const record = Order.create(input)
await orderRepository.save(record)
await auditLogRepository.record(record)
「注文を作成したら、監査ログに書き込むのを忘れないようにしてね」というコードです。
このコードが 1 箇所だけであればいいのですが、異なる処理からも注文を作成する必要が出てきた場合においても、この手続きを守る必要があります。
この手続きのうち 1 行でも抜けると、「注文は存在するのに監査ログがない」という不完全な状態ができてしまいます。どうあるべきか。
たとえば次のように業務操作として名前を付けて、それを使うようにする、などでしょうか。それに加えて、placeOrder の内部処理が直接外部モジュールから呼び出せないようになっていると良いですね。
export async function placeOrder(input: PlaceOrderInput) {
const order = createOrder(input)
await saveOrder(order)
await recordOrderAuditLog(order)
return order
}
function createOrder(input: PlaceOrderInput) {
return Order.create(input)
}
async function saveOrder(order: Order) {
await orderRepository.save(order)
}
async function recordOrderAuditLog(order: Order) {
await auditLogRepository.record(order)
}
利用側は、個別の手順を知らなくてよい。
await placeOrder({
customerId,
items,
requestedBy,
})
あるいは、注文作成処理において「注文が作成された」というイベントを発生するようにし、そのイベントハンドラで監査ログを発行するというイベント駆動型のアプローチもあり得ますね。
具体の解き方は文脈によって変わるものではありますが、大事にすべきなのは保存において監査ログの保存が必要であるというルールを、呼び出し側 (=実装者) が毎回気をつける必要がないことです。これが「実装者を信じない」ということの一例です。
「ありえない組み合わせ」を作れてはいけない
筆が乗ってきたのでもう一例出してみましょう。対応関係のある値を、呼び出し側が自由に組み合わせられる形は避けましょう。
sendNotification({
channel: "email",
phoneNumber: "+819012345678",
body: "..."
})
email 通知なのに phoneNumber を渡せてしまうように、「業務上ありえない組み合わせ」をコード上では作れてしまうこと、ありませんか。
型が { channel: string; email?: string; phoneNumber?: string; deviceToken?: string } なら、明らかにおかしい組み合わせでも通ってしまう。よくないですよね。
sendNotification(emailNotification({
to: emailAddress,
subject,
body,
}))
sendNotification(smsNotification({
to: phoneNumber,
body,
}))
一案としては、通知チャネルと宛先の対応関係は、呼び出し側の注意ではなく型や factory に持たせることも有効です。このようにして、不正な組み合わせを「レビューで見つける」のではなく、「そもそも作れない(ビルドが通らない)」状態にすることが大事です。
同じ話は、イベント名とイベント種別、外部サービスと認証方式、状態と許可される操作のような関係にも当てはまります。
値同士の関係を、設計で守りましょう。「気をつけてね」は設計ではないのです。
AI コーディングでこそ、この原則が有効
繰り返しになりますが、この話は人間がコードを書いていた頃から変わりません。人は..というか、少なくとも私は人一倍に注意力がない人間なので、これまでも「型で縛る」、「不正な状態を作れないようにする」、という設計は非常に重要でしたし、助けられてきました。
そしてこの AI コーディング時代、この原則はより大事になってきています。
レビューする/しない論争も記憶に新しいですが、AI も人間も関係なく、「信用できない実装者」と仕事をするときには「間違えようがない設計」が重要なのです。
一方で、AI には人間より強いところもあります。「言われた通りにやりきる」力です。
人間なら面倒で崩してしまうような設計原則も、AI は疲れずに、文句言わずに徹底してくれます。
例示したような「厳密なルールに守られた設計」は、安全ではあるものの「めんどくさい」コードベースになりがちです。でも、AI はめんどくさがらずにコードを書いてくれる。だから、ルールを徹底できます。
AI は間違えます。だから、間違えようがない設計にしましょう。
しかし、AI は徹底できます。だから、徹底的に設計を研ぎ澄ませましょう。
明日もよろしくお願いいたします。
新体制 Algomatic、採用強化中です。ぜひカジュアル面談お申し込みくださいませ!
原文を表示
こんにちは、Algomaticの菊池です。
Algomatic 初夏のアドベントカレンダー8日目、6本目の投稿となります。(土日はお休みです)
前日分は小笠原さんによる「ハーネスエンジニアリングを、コンサル/PMOの業務に翻訳する」です。生成AIにおける概念を人間活動のメタファーで捉えることで見えてくる視点、おもしろいですよね。
さて、今日の記事では、あまりAI関係ない話をします。実装の話です。
AIの書いた筋の悪いコードに辟易としていませんか。コードレビューで何度同じ指摘を繰り返しているのか...と嘆いている方も多いと思います。でもこれ、AI固有の問題ではありません。
人間によるチーム開発であっても「コードの質」は常々問題になります。
実装前の設計レビューをする、コードレビューによる指摘で実装者の成長を促す、などなど色々な手段はありますが、わたしが最も重要かつ効果が高いと考えるのは「実装者の能力に依存しないコードベース」です。
この記事では、「実装者の能力に依存せず、筋の悪いコードを書けないようにする」ためのいくつかの考え方と例を示します。
実装者の注意力に依存してはいけない
あなたのソースコード、「AしたらBもするようにしましょう」といった「気をつけて守らないといけない手順」ありませんか。
たとえば、ある業務操作(ここでは「注文」という行為とします)を実行するときに、データの作成、保存、監査ログの記録が必要だとしましょう。
こんなコードに見覚えありませんか。
const record = Order.create(input)
await orderRepository.save(record)
await auditLogRepository.record(record)
「注文を作成したら、監査ログに書き込むのを忘れないようにしてね」というコードです。
このコードが1箇所だけであればいいのですが、異なる処理からも注文を作成する必要が出てきた場合においても、この手続きを守る必要があります。
この手続きのうち1行でも抜けると、「注文は存在するのに監査ログがない」という不完全な状態ができてしまいます。どうあるべきか。
たとえば次のように業務操作として名前を付けて、それを使うようにする、などでしょうか。それに加えて、placeOrderの内部処理が直接外部モジュールから呼び出せないようになっていると良いですね。
export async function placeOrder(input: PlaceOrderInput) {
const order = createOrder(input)
await saveOrder(order)
await recordOrderAuditLog(order)
return order
}
function createOrder(input: PlaceOrderInput) {
return Order.create(input)
}
async function saveOrder(order: Order) {
await orderRepository.save(order)
}
async function recordOrderAuditLog(order: Order) {
await auditLogRepository.record(order)
}
利用側は、個別の手順を知らなくてよい。
await placeOrder({
customerId,
items,
requestedBy,
})
あるいは、注文作成処理において「注文が作成された」というイベントを発生するようにし、そのイベントハンドラで監査ログを発行するというイベント駆動型のアプローチもあり得ますね。
具体の解き方は文脈によって変わるものではありますが、大事にすべきなのは保存において監査ログの保存が必要であるというルールを、呼び出し側(=実装者)が毎回気をつける必要がないことです。これが「実装者を信じない」ということの一例です。
「ありえない組み合わせ」を作れてはいけない
筆が乗ってきたのでもう一例出してみましょう。対応関係のある値を、呼び出し側が自由に組み合わせられる形は避けましょう。
sendNotification({
channel: "email",
phoneNumber: "+819012345678",
body: "..."
})
email 通知なのに phoneNumber を渡せてしまうように、「業務上ありえない組み合わせ」をコード上では作れてしまうこと、ありませんか。
型が { channel: string; email?: string; phoneNumber?: string; deviceToken?: string } なら、明らかにおかしい組み合わせでも通ってしまう。よくないですよね。
sendNotification(emailNotification({
to: emailAddress,
subject,
body,
}))
sendNotification(smsNotification({
to: phoneNumber,
body,
}))
一案としては、通知チャネルと宛先の対応関係は、呼び出し側の注意ではなく型や factory に持たせることも有効です。このようにして、不正な組み合わせを「レビューで見つける」のではなく、「そもそも作れない(ビルドが通らない)」状態にすることが大事です。
同じ話は、イベント名とイベント種別、外部サービスと認証方式、状態と許可される操作のような関係にも当てはまります。
値同士の関係を、設計で守りましょう。「気をつけてね」は設計ではないのです。
AIコーディングでこそ、この原則が有効
繰り返しになりますが、この話は人間がコードを書いていた頃から変わりません。人は..というか、少なくとも私は人一倍に注意力がない人間なので、これまでも「型で縛る」、「不正な状態を作れないようにする」、という設計は非常に重要でしたし、助けられてきました。
そしてこのAIコーディング時代、この原則はより大事になってきています。
レビューする/しない論争も記憶に新しいですが、AIも人間も関係なく、「信用できない実装者」と仕事をするときには「間違えようがない設計」が重要なのです。
一方で、AIには人間より強いところもあります。「言われた通りにやりきる」力です。
人間なら面倒で崩してしまうような設計原則も、AIは疲れずに、文句言わずに徹底してくれます。
例示したような「厳密なルールに守られた設計」は、安全ではあるものの「めんどくさい」コードベースになりがちです。でも、AIはめんどくさがらずにコードを書いてくれる。だから、ルールを徹底できます。
AIは間違えます。だから、間違えようがない設計にしましょう。
しかし、AIは徹底できます。だから、徹底的に設計を研ぎ澄ませましょう。
明日もよろしくお願いいたします。
新体制Algomatic、採用強化中です。ぜひカジュアル面談お申し込みくださいませ!
関連記事
Salesforce CodeGen チュートリアル:ユニットテストと安全性チェック付きの Python 関数の生成・検証・再ランク付け
Salesforce は Hugging Face からモデルを読み込み、自然言語から Python 関数を生成するエンドツーエンドワークフローを公開した。この手法には構文チェックや静的解析、ユニットテストによる検証が含まれる。
Mistral AI が Vibe にコード作成とアプリ機能を提供へ
AI 企業「Mistral AI」は、開発者向けプラットフォーム「Vibe」にコード生成およびアプリケーション構築機能を追加すると発表した。これにより、ユーザーは Vibe 上で直接コード作成やアプリ開発が可能になる。
Claude で Replit が利用可能に(2 分読了)
Anthropic の AI チャットボット「Claude」が、コード開発環境「Replit」との連携機能を正式に追加し、ユーザーは Claude 内で直接 Replit を使用できるようになった。
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み