
AIは目的ではなく部品。 ──依存性逆転の原則で考える、振り回されないAIとの付き合い方
本記事では、エンジニアがそれぞれに見つけてきた“AIとの付き合い方”をご紹介します。仕事に取り入れたツールやワークフローから、チームでの使いどころ、あえて自分の手でやると決めた場面まで。うまくいった工夫だけでなく、試して戻したことや思いがけない発見を通して、AIとの距離の取り方をのぞいていきます。AIをどう使うかだけでなく、どんな距離感で付き合っているのか。誰かのAIとの向き合い方が、あなたとAIのちょうどいい"さじ加減"の手がかりに。
新しいAIツールが出るたびに使い方を覚え直し、設定をつくり直し、前のツールで組んだ仕組みを捨てる。この1年、そんな疲れを感じている方は多いのではないでしょうか。
私もそうでした。Claude Codeに新しい機能が入るたびに触り、仕様が変わるたびに手元の設定を書き直していました。気づくと、つくりたいものではなく「Claude Codeを使いこなすこと」に時間を使っていたのです。
今はほとんど振り回されません。ソフトウェア設計の「依存性逆転の原則」を、AIとの付き合い方と生き方に当てはめたからです。
先に結論を書きます。AIを目的として使ってはいけません。人が持つのは目的と本質、つまり抽象だけにして、AIはその抽象に合わせて差し込む部品にします。依存の向きを、人からAIへではなく、AIから人の抽象へと逆にするのです(図表1『依存の向きの逆転』)。

この記事では、依存性逆転を知らない方にも伝わるように、ゲームの車のエンジンと恋愛のたとえで説明します。そのうえで、AI時代の開発で私が実際に何を握り、何を手放しているかを書きます。
自己紹介
改めまして、手羽先です。中小企業から大企業、スタートアップまで、事業の根幹を支えるシステムの設計と開発をしています。
研究では、ニューラルネットワークの学習で長く使われてきた誤差逆伝播法に代わる、新しい学習アルゴリズムの開発に取り組んでいます。ゲームもつくっていて、報道番組のコメンテーターになって世論を動かすノベルゲーム「コメンテーター」を、ジー・モードからNintendo SwitchとSteamで配信する準備を進めています。また、NICT(情報通信研究機構)が主催する若手向けのセキュリティ人材育成プログラム SecHack365 では、AIセキュリティの研究開発をしていました。
業務システム、研究、ゲームと分野はばらばらに見えますが、私の芯は1つです。どんな問題も、まず微分できる問題に解き直します。そこに勾配降下法を当てはめ、目的関数を決めておけば、あとは誤差が勝手に下がり続ける仕組みになります。この最適化の考え方を、AIと機械学習の観点からビジネスに当てはめるのが私の仕事で、その職種を「Business AI Designer」と名乗っています。この記事でご紹介する依存性逆転も、目的関数を人が握り、残りを差し替えられる部品にするための考え方です。
AIを目的にすると具体に振り回される
AIを目的にする、とはどういう状態でしょうか。たとえば「Claude Codeを使いこなしたい」「Cursorのルールを極めたい」という目標を立てた状態です。
この目標は、特定の道具の細部に自分を縛りつけます。仕様が変わるたびに覚え直し、つくりたいものを置き去りにしたまま、覚え直しの輪を回り続けることになります(図表2『道具に振り回されるループ』)。

道具の使い方を誰よりも知っているという優位も、AIに聞けば済むようになり、長く続きません。AIを目的にした瞬間、自分の価値はAIの側の都合で上下します。この状態を抜けるための考え方が、依存性逆転です。
依存性逆転とは何か
まず「依存」という言葉から説明します。AがBを使っていて、Bが変わるとAも直さなければならないとき、「AはBに依存している」と言います。Aが大事な方針でBが細かい道具なら、道具を替えるたびに方針の側まで書き直すことになります(図表3『依存とは何か』)。

依存性逆転の原則(Dependency Inversion Principle、DIP)は、ソフトウェア設計の原則の1つです。上位のモジュールは下位のモジュールに依存してはならず、どちらも抽象に依存すべきだ、というものです。提唱者のRobert C. Martinは、のちにクリーンアーキテクチャの解説で、ソースコードの依存は内側(大事な方針の側)にだけ向けるという「依存のルール」として整理しています。
もう1つ、依存性の注入(Dependency Injection、DI)という言葉もよく一緒に出てきます。使う部品を自分で作らず、外から渡してもらう書き方のことです。Martin Fowlerの記事で名前が付けられ、広まりました。
ここからは、レースゲームの車を例にコードで見てみます。プレイヤーの車は、エンジンの性能で加速が決まるとします。まず、DIPをしない素直な書き方です。
// ❶ DIPしない:車体が特定のエンジンを自分で作る
class GasEngine {
accelerate(dt: number) { return 8 * dt; } // 燃料や音の処理もここにある
}
class Car {
private engine = new GasEngine(); // 車体の中で具体を new している
speed = 0;
update(dt: number) { this.speed += this.engine.accelerate(dt); }
}
これでも動きますが、アイテムでターボに替えたい、EVの車種も足したい、という要望をこの形のまま実装すると、次のようになります。
// ❶' DIPしないまま拡張すると、車体がすべてのエンジンを知ることになる
class Car {
private gas = new GasEngine();
private turbo = new TurboEngine(); // 足すたびに Car を開いて書き換える
private motor = new ElectricMotor(); // EV を足したらまた Car を書き換える
constructor(private engineType: "gas" | "turbo" | "ev") {}
speed = 0;
update(dt: number) {
if (this.engineType === "gas") this.speed += this.gas.accelerate(dt);
else if (this.engineType === "turbo") this.speed += this.turbo.accelerate(dt);
else this.speed += this.motor.accelerate(dt); // 4種類目が来たら分岐がまた増える
}
}
// テスト:速度の計算だけ確かめたいのに、本物のエンジンが燃料を減らし音を鳴らす
const car = new Car("turbo");
car.update(1);
expect(car.speed).toBe(14); // エンジンの数値を調整した瞬間に、このテストも壊れる
エンジンを1種類足すたびにCarを開いて、分岐とフィールドを書き足すことになります。速度の計算だけを試したくても、本物のエンジンの燃料や音の処理まで一緒に動き、エンジン側の数値を調整しただけでCarのテストが壊れます。車体の担当とエンジンの担当が別の人なら、2人とも同じCarのファイルを書き換えるので、同時に作業すると衝突します。
次に、DIPとDIで書いた形です。
// ❷ DIP:車体の側が「エンジンの規格」を決める
interface Engine {
accelerate(dt: number): number; // dt 秒で増える速度
}
// 車体は規格だけを知り、エンジンは外から渡してもらう(DI)
class Car {
speed = 0;
constructor(private engine: Engine) {}
update(dt: number) { this.speed += this.engine.accelerate(dt); }
}
// エンジンの側が規格に合わせる
class GasEngine implements Engine { accelerate(dt: number) { return 8 * dt; } }
class TurboEngine implements Engine { accelerate(dt: number) { return 14 * dt; } }
class ElectricMotor implements Engine { accelerate(dt: number) { return 11 * dt; } }
// 組み立ては外で行う。アイテムを取ったらエンジンを差し替える
const car = new Car(new GasEngine());
const boosted = new Car(new TurboEngine());
// テストでは、決まった値を返す偽物のエンジンを差し込む
class FakeEngine implements Engine {
constructor(private v: number) {}
accelerate() { return this.v; }
}
const testCar = new Car(new FakeEngine(100));
testCar.update(1); // speed は必ず 100。燃料や音の処理は動かない
このコードと車の絵を並べると、対応がはっきりします(図表4『TSのコードと車の対応』)。interface Engineがエンジンルームの規格、Carが車体、implements Engineが規格に合わせてつくったエンジン、new Car(new TurboEngine()) が組み立て、つまり注入です。

2つの書き方の挙動の差は3つです(図表5『DIPしない車とDIPした車』)。1つめに、ターボやEVのエンジンを足してもCarは1行も変わりません。2つめに、偽物のエンジンを差し込めば、速度の計算だけを確実に試せます。3つめに、エンジンと車体の担当がinterfaceを約束として決めておけば、別々に並行して作れます。

一言でまとめると、大事な側がルールを決め、細かい側がそれに合わせるのが依存性逆転です。
車体とエンジンで考える
コードを読まない方のために、絵で考えます。車体を特定のエンジンの形に合わせてつくると、エンジンを替えるとき車体までつくり直しになります。逆に、車体の側が「エンジンルームはこの大きさの四角形、端子はこの形」と規格を先に決めておけば、規格に収まるエンジンならどれでも載せられます。ガソリンのエンジンを電気のモーターに替えても、車体はそのままです(図表6『車体とエンジンルームの規格』)。

ポイントは、規格を持っているのがエンジンではなく車体だということです。普通は車体が手元のエンジンに引っぱられますが、それをひっくり返して車体が規格を決める。これが「逆転」の意味です。
規格の形は、四角だけではありません。丸、凹凸のある形、菱形など、いくつもの形の規格を用意できます。大事なのは、同じ形でさえあれば中身が違っても差し込めること、そして形が違うものは最初から入らないことです(図表7『形の規格と差し込める部品』)。ソフトウェアでは、この形がinterfaceで、中身が実装です。TypeScriptなら、形の合わない部品を渡すと、動かす前のコンパイルの時点で型のエラーになります。入れ替えの簡単さと、間違ったものが入らない安全さを、同じ規格が両方引き受けます。

Interfaceを挟むとなぜ依存が逆転するのか
クリーンアーキテクチャでは、コードを層に分けます。リクエストを受けて返すpresentation、処理の流れを組み立てるapplication、業務のルールそのものを表すdomain、データベースや外部サービスとつなぐinfrastructureです。1番大事なのはdomainで、ソースコードの依存はすべてdomainに向けます(図表8『層と依存の向き』)。

ところがUserServiceがMySQLの処理を直接呼ぶと、内側が外側に依存し、データベースを替えるたびにUserServiceまで直すことになります。
そこで間にinterfaceを挟みます。「ユーザーをこう取り出して保存できる」という規格だけをUserRepositoryとしてdomainに置き、UserServiceはそのinterfaceだけを知るようにします。MySQLの実装は、そのinterfaceをimplementsしてつくります。
// domain:内側が規格を持つ
type User = { id: string; name: string };
interface UserRepository {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
}
// application:規格だけを知っている
class UserService {
constructor(private repo: UserRepository) {} // 外から渡してもらう(DI)
async rename(id: string, name: string) {
const user = await this.repo.findById(id);
if (!user) throw new Error("ユーザーが見つかりません");
await this.repo.save({ ...user, name });
}
}
// infrastructure:外側が規格に合わせる
class MySQLUserRepository implements UserRepository {
async findById(id: string) { /* MySQL に SELECT */ return null; }
async save(user: User) { /* MySQL に UPDATE */ }
}
ここで矢印を見比べてください(図表9『Interfaceを挟む前と後』)。制御の流れは前と同じくUserServiceからMySQLへ、内から外へ向かいます。一方でソースコードの依存は、MySQLUserRepositoryの方が「implements UserRepository」と書いてdomainを知りに来ています。外向きだった矢印が、interfaceを挟んだことで内向きにひっくり返った。これが「逆転」です。

具体的な場面で考えます。運用中のサービスのデータベースを、MySQLからPostgreSQLに移すことになったとします。interfaceがなければ、移行の担当者はUserServiceに散らばったMySQL向けの書き方を、業務の流れごと読み解いて書き換えることになり、その間は機能の追加も止まります。
interfaceがあれば、移行の担当者はPostgresUserRepositoryをつくることだけに集中し、サービスの担当者は今まで通りUserServiceを育て続けられます。担当者が違っても、守る約束はUserRepositoryの形だけです。最後に組み立てる場所で1行を替えれば切り替わります(図表10『MySQLからPostgresへの移行』)。
class PostgresUserRepository implements UserRepository {
async findById(id: string) { /* PostgreSQL に SELECT */ return null; }
async save(user: User) { /* PostgreSQL に UPDATE */ }
}
// 組み立て(main):ここだけを替える
// const service = new UserService(new MySQLUserRepository());
const service = new UserService(new PostgresUserRepository());
同じテストをMySQL版とPostgres版の両方に流して結果がそろえば、移行が正しいことも確かめられます。

逆転すると、外側の変更は外側で止まります。データベースやテスト用の偽物に差し替えても、WebフレームワークをEchoからGinに替えても、domainとapplicationは変わりません(図表11『差し替えても内側は変わらない』)。

恋愛で考えると依存の痛さがわかる
付き合っている相手の具体、たとえば相手の今の言動や好みそのものに自分の気持ちを全部預けると、とても辛くなります。具体的には、次のような状態です。
- デート: 行く店も観る映画も、いつも相手の好みに合わせて決めている。相手が別のものを好きになった途端、どこに行けばよいのか分からなくなり、自分が何を楽しいと思っていたのかも思い出せない。
- 生活: 起きる時間や休日の過ごし方を、相手の予定に全部合わせている。相手の仕事が忙しくなって会えない週が続くと、自分の1日の組み立てごと崩れてしまう。
- 会話: 相手の返信の速さや、ひと言の温度で、その日の気分が決まる。返信が少し遅れただけで不安になり、自分の作業が手につかない。
どれも、相手が少し変わるだけで自分が揺れる状態です。関係が終わったときには、自分が何を大事にしていたのかまでわからなくなります。これは、車体を特定のエンジンに合わせてつくった状態と同じです。
そうではなく、自分の理想像を抽象として言葉にしておきます。「一緒に挑戦を面白がれる」「約束を守る」「お互いの時間を尊重できる」といった、相手に求める条件ではなく、自分が関係の中で大切にしたいことの一覧です(図表12『具体への依存と抽象への依存』)。

抽象の細かさで考えると、違いがよく分かります。たとえば「カフェでゆっくり話す時間が楽しい」という抽象を持っていれば、一緒にいる相手が変わっても、行く店が木造の古いカフェから洋風のカフェに変わっても、その時間を楽しめます。反対に「木造のカフェ」「前の相手とよく行ったあのカフェ」と条件をどんどん具体にしていくと、当てはまる人も場所も減っていきます。理想に近い人に出会えないのは、自分で選択肢を狭めているからかもしれません。
これはDIと同じ形です。自分が守りたい型(interface)さえ決まっていれば、そこに入ってくる具体を1つに決め打ちしなくて済みます。言い方は少し乱暴ですが、型がはっきりしていれば、誰と出会っても、その人との関係を自分の型に照らして組み立てられるということです。
さらに、テストもできます。コードでFakeEngineを差し込んで速度の計算だけを確かめたように、頭の中で「仮の相手」を自分の型に差し込んでみるのです。
// 自分が関係の中で大切にしたいこと(型)
interface Partner {
enjoysChallenges(): boolean; // 一緒に挑戦を面白がれる
keepsPromises(): boolean; // 約束を守る
respectsTime(): boolean; // お互いの時間を尊重できる
}
// 頭の中で差し込む仮の相手(テスト用の偽物)
class FakePartner implements Partner {
enjoysChallenges() { return true; }
keepsPromises() { return true; }
respectsTime() { return false; } // ここだけ満たさない人だったら?
}
const week = simulateMyWeek(new FakePartner());
// 平日の作業時間は守れるか、週末の予定はどう変わるか
たとえば「約束は守るけれど、お互いの時間は尊重しない人」を差し込んで、自分の1週間がどう変わるかを想像します。平日の夜に作業する時間が消えて困るなら、自分にとって「時間の尊重」は外せない項目だとわかります。思ったほど困らないなら、その項目は型から外してもよいのかもしれません。
これがあれば、細かな違いに振り回されず、別れることになっても自分の軸は残ります。相手を物として扱う話ではなく、自分が何を大切にしているかを軸にする話です。
人は、抽象を持たないまま具体に依存すると苦しくなります。これはAIとの付き合いでも同じです。
AIとツールをDIPする手順
ここからは、Figma・Claude Code・Codexといった実際の道具のDIPを、明日から始められる形で書きます。
現場で依存の向きを逆にした2つの例
手順に入る前に、私の現場で依存の向きを逆にした例を2つ紹介します。1つめは、人が抽象を持ち、具体はAIが差し込む実装として扱うDIPの例、2つめは、人が目的関数だけを握り、最適化をAIに回させる例です。
1つめは、あるアプリのストア申請です。Google Play ConsoleやApp Store Connectの画面は、メニューの位置も質問の並びも更新のたびに変わります。申請を画面の操作手順として覚えると、上位の判断が下位の具体である画面に依存し、UIが変わるたびに手順を覚え直すことになります。そこで依存の向きを逆にし、人は2つの抽象だけを書きました。1つは、コードのどこを変えたら、どの書類とどの申告欄に波及するかという対応で、Claude Codeのスキルとして持っています。もう1つは、判断が要る項目と、審査の期限から逆算した工程です。画面の操作はこの抽象をimplementsする実装として扱い、Claude in Chromeにブラウザで入力させ、毎回同じになる操作はPlaywrightのスクリプトに落としました。両ストアの申請は4日で完了し、審査で20項目ほどの差し戻しを受けたときも、同じ抽象を渡して再申請まで任せられました。実行役が人でも、ブラウザを操作するAIでも、スクリプトでも、規格の側は1行も変わりません。
2つめは、UIの実装を目的関数で回した例です。デザインとの1ピクセル単位の位置合わせは、人が目で見比べている限り、AIに任せても「どこまで直せば終わりか」を判定できません。あるプロジェクトでは、この位置合わせが作業時間の半分以上を占めていました。そこで画面の部品ごとにIDを振り、デザイン上の位置と実装の位置のずれを数値で返すようにしました。これは機械学習でいう目的関数です。人が握るのは「部品ごとの正しい位置」という規格と、ずれを測る式だけで、どのエージェントがどう直すかは問いません。ずれが縮むまでClaude Codeに直させる作業は夜のうちに回し、朝に結果を確かめます。同じ判定をPlaywrightのビジュアルリグレッションテストとして残しておけば、エージェントを別の道具に替えても、良し悪しは同じ目的関数が決めます。
先にゴールを示すと、時間の使い方がこう変わります(図表13『時間の使い方の前と後』)。覚え直しの時間が減り、空いた時間を本当の問題を言葉にすることや設計の判断に使えます。

まず今の依存の地図です。何を作るかの仕様はチャットの履歴とプロンプトの小技の中に散らばり、守ってほしい決まりはツール専用の設定ファイルにだけ書かれ、色や余白の決まりはFigmaの中にしかありません。この形のままClaude CodeからCodexに乗り換えると、次のことが起きます。
# DIPしないまま乗り換えると壊れるもの
CLAUDE.md ← 仕様・規約・禁止事項がすべてここにだけある
.claude/settings.json ← 「本番のDBに触らない」はここのフックだけが止めている
(残りの仕様はチャットの履歴の中)
→ Codex は既定で AGENTS.md を読み、CLAUDE.md も .claude/ の設定も読まない
→ 規約を一から書き直す/禁止事項は止まらない/仕様は会話を遡って探す
Codexの公式ドキュメントによると、Codexが指示として読むのはAGENTS.md(と設定で足した名前のファイル)だけです。逆にClaude Code は AGENTS.mdを読め、CLAUDE.mdがあるときは中に@AGENTS.mdと書けば取り込めます。この性質を使うと、依存の向きを逆にできます。仕事の種類ごとに差し込み口(型)を先に決め、ツールは薄いアダプターを通してそこに差し込む部品として扱うのです(図表14『ツールのDIPの全体像』)。実装、デザイン、ストアへの申請、守らせたい決まりのどれも、型が共通なら差し込むツールは入れ替えられます。

やることは、車体の規格にあたる抽象をツールの外、つまりリポジトリの中に置き、ツールごとの設定はそれをつなぐだけの薄いアダプターにすることです(図表15『リポジトリに置く抽象と薄いアダプター』)。

- 目的と仕様: 何をつくるか、誰の何を解くか、ドメインの言葉をdocs/のMarkdownに書きます。
- テスト: E2Eとビジュアルリグレッションテストを目的関数として置きます。どのツールが書いたコードでも、良し悪しはここで決まります。
- 共通の指示: ビルドの手順や規約はAGENTS.mdに寄せます。CLAUDE.mdは@AGENTS.mdの1行と、Claudeだけに要る数行にとどめます。手順書(Skills)も、CodexとClaude CodeがどちらもSKILL.mdという同じ形式を読めるので、中身は1か所にまとめ、置き場所の違いだけをアダプターで吸収します。
- デザインの決まり: 色・余白・文字の大きさはFigmaの変数としてそろえ、コード側にもトークンとして持ちます。FigmaのMCPサーバーを使えば、変数やコンポーネントをAIに渡せます。
- 必ず止めたい操作: 判定のスクリプト本体はscripts/に置き、各ツールのフック設定からはそれを呼ぶだけにします。CLAUDE.mdの文章は守らせたい文脈であって強制ではないので、止めたいものはフックで止めます。
コードで書くと、図表5のCarとエンジンと同じ形です。
// 規格:仕様を渡すと、変更(PR)が返ってくる
interface CodingAgent {
run(spec: Spec): Promise<PullRequest>;
}
class ClaudeCodeAgent implements CodingAgent { /* CLAUDE.md → @AGENTS.md */ }
class CodexAgent implements CodingAgent { /* AGENTS.md をそのまま読む */ }
class FakeAgent implements CodingAgent { /* 人が手で書く、または小さなモデル */ }
// 判定は道具ではなくテスト(目的関数)が行う
async function deliver(agent: CodingAgent, spec: Spec) {
const pr = await agent.run(spec);
return runE2E(pr); // 通らなければ、直すのは道具ではなく仕様かテスト
}
例1 ストア申請は画面ではなく申請の答えに依存させる
アプリをGoogle Playストアに出すとき、出し先そのものは当分変わりません。変わるのはPlay Consoleの画面です。メニューの場所や質問の並びは、更新のたびに少しずつ変わります。困るのは、申請の答えが担当者の頭の中や、当時の画面のスクリーンショットにしか残っていないときです。
# DIPしないまま画面が変わると起きること
申請の答え ← 前回の担当者の記憶と、当時の画面のスクショだけ
画面の更新 → スクショと位置が合わず、どの質問の答えだったか分からない
担当者の交代 → 「ユーザー同士のやり取りはあったか」から調べ直し
機能の追加 → レーティングの質問票を出し直すべきか、誰も判断できない
そこで、申請の答えのほうを抽象として、リポジトリに store.yaml のような形で持っておきます(図表16『ストア申請の抽象と実行役』)。ユーザー同士がやり取りできる機能はあるか、電気通信事業法の届出は要るか・出したか、対象年齢のレーティングの質問票への答え、集めるデータと使い道、といった項目です。

どれも一度決めたら終わりではありません。Google Playはすべてのアプリに IARCのコンテンツのレーティングを求めていて、内容や機能が変わって答えが変わるときは質問票を出し直す必要があります。集めるデータはデータ セーフティとして原則すべてのアプリで申告します。他人の通信を媒介する事業には電気通信事業の登録や届出が要る場合があり、自分のアプリが当たるかは総務省の参入マニュアルで確かめます。答えが抽象として残っていれば、機能を足したときに「どの答えが変わるか」を差分で見られます。
これさえあれば、画面を操作する役は差し替えられます。今は人が手で入力し、画面が変わって面倒になったら、ブラウザを操作するAIにstore.yamlを渡して入力させます。ビルドのアップロードや掲載情報の更新のように公式のAPIがあるものはAPIに、MCPが出てきたらMCPに任せます。UIが変わっても書き直すのは実行役だけで、申請の答えは変わりません。App Store Connectに出すときも、同じ答えを別の画面に差し込むだけです。
例2 デザインのやり取りはツールではなくルールに依存させる
Figmaには次々と新しい機能が入りますし、いずれFigmaより良いと感じるツールが出てくるかもしれません。ここでも依存させる先を、ツールの機能ではなく、チームのコラボのルールにします(図表17『デザインコラボのルールと移せるツール』)。たとえば「デザインへの指摘や質問は、自作の吹き出しのコンポーネントでやり取りする」というルールです。吹き出しには未対応・対応中・完了の状態を持たせ、色・余白・文字の大きさはトークンから使う、といった決まりも添えておきます。

悪い形は、ルールが特定のツールの機能の使い方として、人の手順にしか残っていない状態です。
# ルールがツールの機能の使い方として人の手順にだけある
「Figma のコメントで @ を付けて、解決したらチェックを押す」
→ 別のツールに移ると、やり取りの履歴も「解決」の状態も持っていけない
→ 新しいツールで同じやり方を一から決め直し、移行の間はやり取りが二重になる
ルールを言葉と部品として持っておけば、Adobeの製品など別のツールに移っても、AIに「このルールをこのツールで実現する方法」を調べさせ、画面を操作させたり、MCPやCLI、APIがあればそれで組ませたりできます。新しい機能は、ルールを満たすのに便利なら使えばよく、振り回される理由にはなりません。
2つの例は、図表14の帯の1本ずつです。型が共通だから、差し込むツールは何度でも入れ替えられます。
読者のみなさんが明日から進めるなら、次の5つの順番がおすすめです(図表18『明日から始める5つの手順』)。

- 目的と基準を書く。今のプロジェクトの目的・仕様・ドメインの言葉をdocs/に書き出します。
- テストで目的関数をつくる。1番価値のある流れからE2Eを1本書きます。
- 共通の指示をAGENTS.mdに寄せる。ツール専用の設定に書いた規約を移し、設定は薄くします。
- 使い方はAIに聞き、調査は任せる。新しい機能や道具の調べものはAIや仲間に頼み、自分は規格を満たすかだけを判断します。
- 差し替えテストをする。同じ仕様を別のツールに渡して、テストが通るかを確かめます。通らなければ、足りないのは道具ではなく抽象の側です。
道具を使うか迷ったときは、「明日この道具が消えても、目的と基準が手元に残っていれば、別の道具で同じものをつくり直せるか」と自分に問います。答えが「はい」なら、依存の向きは正しく逆になっています。
本質は目的関数として書いておく
機械学習は大きく4つの部品でできています。モデル(構造)、入力データと正解ラベル、目的関数(どれだけ間違えたかを測る式)、学習アルゴリズム(間違いを減らす方向に重みを直す手順)です。出力と正解ラベルの誤差を目的関数で測り、学習アルゴリズムが誤差を減らす方向へ重みを直す。この輪が回って学習が進みます(図表19『機械学習の学習の流れ』)。

開発も同じ形に見えるようになりました。最近は、数値の微分ではなく、文章で「どこがどう悪いか」を返して改善を回す研究も出ています(TextGradの論文)。コーディングエージェントがテストの失敗を読んでコードを直していくのは、まさに誤差を見て重みを直す学習そのものです。
そう考えると、人が力を入れるべき場所と、張り付いてはいけない場所がはっきりします(図表20『機械学習の4つの部品と人の持ち場』)。

- ❶目的関数=テスト。1番厚くすべき場所です。
- ❷入力データ=要件とドメイン。現場の理解は、今のところ人の方が得意です。
- ❸モデル=設計とアーキテクチャ。良い構造は公開されて広まるので、人は選ぶ判断だけをします。
- ❹学習アルゴリズム=プロンプトやハーネスの細かな工夫。誰もが同じ工夫をするので、張り付いても差がつきません。
依存性逆転の言葉で言い直すと、❶と❷が車体の規格、❸と❹は差し替えられるエンジンです。
私が実際に厚くしている部分
前の章の手順を、私自身はこう配分しています(図表21『私が厚くしている部分』)。

- 厚く(目的関数・入力データ): PlaywrightのE2Eとビジュアルリグレッションテスト、仕様とドメインの言葉のMarkdown、DBのスキーマ変更のチェックリスト、関係図やユースケース図、現場での聞き取り。
- 薄く(学習アルゴリズム): PRやマージはAIに任せ、起きたトラブルや方針はSkillsとHooksに書き込んで、次からAIが自分で守れるようにする。
- 定番を使う(モデル): 機能のブランチごとにDBのブランチと作業場を分け、AIを並行して動かす。
人生も抽象に依存させる
会社、肩書き、技術スタック、道具。こうした具体に自分を依存させると、それが変わるたびに自分が崩れます。だから、自分の目的関数を書いておきます。自分は何を大事にしていて、何ができたら良しとするのか。これが言葉になっていれば、会社が変わっても、使う技術が変わっても、AIがどれだけ賢くなっても、それを規格として新しい具体を差し込めます(図表22『抽象に依存する生き方』)。

「AIのさじ加減」とは、AIに何をどこまで任せるかの話だと思われがちです。私にとっては逆で、自分が何を手放さないかを決める話です。抽象を握り、具体は差し替える。そうすると、AIがどれだけ速く変わっても、それは振り回される理由ではなく、自分の車に新しいエンジンが届く楽しみになります。
参考にした一次情報
- Play Consoleヘルプ「コンテンツのレーティング」(2026年10月確認)
- Play Consoleヘルプ「Google Playのデータ セーフティ セクションの情報を提供する」(2026年10月確認)
- Google Play Developer API「Edits」(2026年10月確認)
- 総務省「電気通信事業参入・変更手続の案内」(2026年10月確認)
- Cursor「Rules」(AGENTS.mdへの対応)(2026年10月確認)
- Claude Code Docs「How Claude remembers your project」(CLAUDE.md・AGENTS.md・@ インポート)(2026年10月確認)
- OpenAI「Custom instructions with AGENTS.md」(Codex)(2026年10月確認)
- OpenAI「Agent Skills」(CodexのSKILL.md)(2026年10月確認)
- Figma ヘルプ「Guide to the Figma MCP server」(2026年10月確認)
- ジー・モード「東京ゲームダンジョン13 出展決定」(PR TIMES、2026年7月30日)(2026年10月確認)
- Robert C. Martin「The Clean Architecture」(2026年10月確認)
- Martin Fowler「Inversion of Control Containers and the Dependency Injection pattern」(2026年10月確認)
- NICT「2025年度SecHack365受講生募集」(プレスリリース)(2026年10月確認)
- Yuksekgonul ほか「TextGrad: Automatic "Differentiation" via Text」(arXiv:2406.07496)(2026年10月確認)