"AI駆動の開発におけるボトルネックは、決してAIの能力ではありません。
いつも問題なのは、人間の判断の質です。"
はじめに
ほぼ二十年前、私の頭の中にある考えが根を下ろしました——しつこく頭から離れない、数か月ごとにふとよみがえっては、また人生が邪魔をする前に『もしも?』とささやく類のものです。私はそれをそこに置いておく理由には事欠きませんでした。締切と不安の下に埋もれていました。私はアーキテクトではなく開発者でした。構想はあったけれど、滑走路がなかったのです。
そして、何かが変わりました。
これから語るのは短い物語です。登場人物は架空で、会社は私が作り出したものですが、その旅は私自身のものです。あらゆる苛立ちも、あらゆるブレイクスルーも、『自分が今まったく何をしているか分かっていない』その瞬間も、すべて現実のどこかに根を持っています。ひと月足らずで、その二十年越しのアイデアは、完全に設計されたAPI、Reactアプリケーション、そしてPWAになりました。私が突然別の人間に生まれ変わったからではありません。ようやく、並んで考えてくれる相棒を得られたからです。
その相棒は、AIでした。
正直にお伝えしたいです。最初から完璧にうまくいったわけではありません。日曜の朝を千回繰り返しても自分一人ではほどけなかったであろうバグがありました。すべてを疑った瞬間もありました。ですが、考えを整理して話せる相手、あるいは何かがいるだけで、その差は言い過ぎではないほど大きいのです。メールを書いたり、提案書を磨いたりするAIではありません。問題の中で一緒に座り、適切な問いを投げかけ、どんな個人の人間にも到底運べない深さの知識から引き出してくれるAIです。
私は以前、自分の仕事が脅かされるのだと信じていました。機械が立ち上がる道のどこかで、残りの私たちが時代遅れになるのだと。ですが、今はそうは思いません。私が今信じているのはこういうことです。AIを『近道』ではなく『協働者』として考える相手——思考パートナーとして扱うことを学ぶ開発者、マネージャー、そしてチームは、ひとりでは決して作れなかったものを作り上げるでしょう。
昔からある言葉があります。今ほど関連性が高いことはありません——『garbage in, garbage out(入れたものがそのまま出る)』。得られるものの質は、完全に、あなたが持ち込むものの質に依存します。文脈がすべてです。ディテールがすべてです。明確さをもって会話に参加すれば、会話の内容はあなたを驚かせます。
これは、私がどうやってその場に現れたのか、という物語です。
プロローグ
2月の木曜の午後、メールが届きました。プロジェクトのステータス報告書と、前回の会議についての会議の招待状の間に埋もれるようにして。
マーカス・ウェッブは、ほとんどそれに気づきませんでした。
件名: FieldOps イニシアチブ: **対応が必要**
彼は二度読みました。そしてノートパソコンを閉じ、自宅の仕事部屋の窓へ歩いていき、そこで約30秒ほど、特に何も見ないまま立ち尽くしました。
デスクに戻ると、彼はまたノートパソコンを開き、三度目に読みました。二度目では、その内容が不条理さを少しも減らしていなかったからです。
彼の上司——Crestline Systemsのアプリケーション開発ディレクター——が、FieldOpsプロジェクトを彼に渡すことになったのです。丸ごとです。3人のビジネスアナリストが4か月かけて仕様を詰めたプラットフォーム。営業チームがスライド資料とバズーカのようなスクリーンショットで見込み客にデモしていたもの。さらに、プロフェッショナルサービス担当のVPが、この1年間の全社会議のたびに『戦略的な差別化要因』だと言い続けてきたもの。
それを。彼一人で。
タイムライン: Q2末までにソフトβの目標。
マーカスは計算しました。14週間。
彼は11年間ソフトウェアを作ってきました。この種のプロジェクトが実際のチームでいくらかかるのか——最低でも開発者4人、6か月、たぶんそれ以上——を分かっていました。FieldOpsについては、計画段階で2度の失敗を見てきました。人員見積もりがいつも高すぎることが原因で、計画が頓挫したのです。
そして今、上司は、十分な人数を見つけることが問題なのではなく、いつだって正しい人材を見つけることが問題だったかのように、それを彼に提示してきたのです。
彼は、すぐに返信することを考えました。機会に感謝しつつ、率直に資源面での懸念を浮かび上がらせるような、慎重でプロフェッショナルな返答を。彼はキャリアの中でその種のメールの文面を何度も書いてきました。何一つ変わったことはありません。
代わりに、彼は新しいブラウザタブを開きました。
彼はAtlasを6か月使っていました。最初は慎重に。新しい道具を使うときのように。そして、それが実際に何なのかを理解するにつれ、より意図的に。補完(オートコンプリート)ではありません。検索エンジンでもありません。もっと名付けがたい何か。決して疲れず、追いかける会議もなく、まあ、前のチームを考えると……といった文で文を始めることのない思考パートナー。
彼は最初のメッセージを打ち込みました。
いま、4人チームと6か月が必要なはずのプロジェクトを任されました。14週間で、しかも一人です。返事をする前に、これが可能かどうかを確かめる必要があります。
彼はEnterを押しました。
Atlasは数秒で返してきました。
ATLAS SESSION 1
2月: 命令(Mandate)
"プロジェクトについて教えてください。"
マーカスは背もたれにもたれました。どこから話せばいい。
これはフィールドサービス管理プラットフォームです。サービス会社、HVAC、配管、電気。ディスパッチャーが、現場の技術者に作業指示書を割り当てます。技術者にはモバイルアプリが必要です。ビジネス側は請求、在庫管理、顧客管理を求めています。マルチテナントで、複数の会社に販売したい。自分たちだけが使うのではなく。単純なプロダクトではありません。
"そうじゃありません," Atlasは同意しました。"そうです。ですが、何かを決める前に2つの問いを分けましょう。1つ目は、この文脈での『可能』とは何か。依頼している人たちにとって、成功した結果はどんなものか。2つ目は、実際の作業は何を必要としているのか。これらの問いは別の答えを持ちます。今あなたはそれらを同じ問いとして扱っているからこそ、『無理だ』と感じる数字になっているのです。"
マーカスは眉をひそめました。そんなふうには考えていませんでした。
わかった。それでは、彼らにとって成功とは何ですか?
"あなたが説明してくれた内容からすると、ソフトβです。実際のユーザー、スコープは限定、プラットフォームが本格的に機能をすべて備える前に動くことを証明するようなもの。これは、すべての機能が完成した、プロダクション運用に耐えるシステムとは別のターゲットです。ソフトβで何がスコープに含まれると仕様書は言っていますか?"
マーカスは4か月分の分析ドキュメントを開きました。前に読んでいました。けれど今は別の読み方です。
中核となるディスパッチャーのフロー。チケット作成と割り当て。技術者向けモバイル画面。基本的な請求。顧客レコード。彼らは在庫と請求の機能が『フェーズ2』だと明確に言っていました。
"つまり、ソフトβのスコープは、フルプロダクトの一部ですね。では、どれくらいの一部ですか?"
マーカスは優先度付けのマトリクスをざっと見ました。『全機能リストのたぶん40%』くらいだ。
"では問いは、14週間がFieldOpsを作るのに足りるかどうかではありません。問いは、『FieldOpsが本物だと証明するための40%を作るのに、14週間で足りるかどうか』です。これは、実質的にまったく別の問いです。あなたの直感ではどうですか?"
直感では、まだ強気すぎる。でも、狂ってはいない。
"答える前に," Atlasは続けました。『1つの前提を押し返したいです。あなたは『一人だ』と言いました。それは本当に正しいですか? もし、私みたいなものが、働く時間すべての間ずっと使えるなら——あなたが眠っている間もコードベース全体の文脈を保持し、あなたが10分で書くのにかかる時間で100行の正しいコードを生成し、エラーメッセージをフレームワークのドキュメントと照合して数秒で、厄介なEF Coreの設定問題をデバッグできるなら——『一人』という言葉は、これまでの意味とは違ってくるかもしれません。』
マーカスは少しの間、それを受け止めました。
『つまり、私は実際には一人のチームじゃない、と言っているんですね。』
"会計の前提が変わったんです。問題は、『1人の開発者が何を作れるか』というあなたのモデルを更新できているかどうかです。"
それは、AIについて語る人々の話を聞いてきたこの2年間で、誰もが彼に対して言った中で最も率直なことだった。
彼は翌朝、マネージャーからのメールに返信した。イエスと返した。
第1章
最初の1週間――コードの前にアーキテクチャ
Marcusが11年以上、数えきれないほどのプロジェクトを経験する中で身に染みて学んだルールはこれだった。コードを書き始める前のアーキテクチャに費やした1時間は、あとでリファクタリングに3時間を節約する。
前の会社はこのルールを無視していた。今の会社はプロジェクトのテンプレートではそれをもっともらしく扱っているが、実際には無視していた。どのプロジェクトも最初に2日間のスプリント計画セッションと、ログインページを作る や データベースを作成する といったタスクの並ぶチケットボードから始まる。まるで、実装すべき内容を決める前に実装を計画できるかのように、また、システムの構造がどうあるべきかを決める前に計画できるかのように。
FieldOpsは違うものになるはずだった。ソフトウェアアーキテクチャについてのマニフェストがあるからではない。以前に同じ過ちを犯したからで、同じことは二度としないと決めていた。
最初の朝、彼はIDEを開かなかった。ドキュメントを開き、書き始めた。
FieldOpsとは、正確には何? それはどんなことを知っていて、そうしたものは何ができるの?
彼は20分間、止まることなくタイピングした。サービスチケット、クライアント、技術者、在庫、請求書。これらが「もの」だ。だが「もの」以上に重要だったのは、それらの間の関係性だった。チケットはクライアントに属し、技術者に割り当てられ、部品を使えば在庫アイテムを消費し、作業が完了すれば請求書が生成される。これらの関係こそがドメインだった。関係を間違えれば、その上に作られるあらゆる機能もまた間違ってしまう。
Atlasを会話に持ち込んだとき、最初の質問は技術の話ではなかった。
技術スタックの話をする前に、ドメインモデリングについて話したい。私には5つの中核となる概念がある。チケット、クライアント、技術者、在庫、請求書だ。これらの境界について、どう考えるか順を追って説明して。
続いたのは2時間で、ツールを使っているというより、すべてを読み尽くして何一つ忘れていないシニアアーキテクトとの設計レビューのように感じられた。Atlasは、クライアントとチケットを同じバウンドコンテキストに入れるという彼の最初の直感に対して押し戻した。そこで投げられたのは、彼がそもそも聞こうと考えていなかった質問だった。チケットの状態に対する権限を持つのは誰? 技術者はチケットをキャンセルできるの? それともディスパッチャだけ? 部品が返却されたら在庫はどうなる? さらにAtlasは、彼が読んだことで知っていたが、大規模に実装したことはなかったパターンを提案した。明示的な不変条件を持つ集約ルート、コンテキスト境界をまたぐための仕組みとしてのドメインイベント、コマンドに使うモデルとクエリに使うモデルの間のきれいな分離。
2時間が終わる頃には、Marcusはホワイトボードに書けそうな量の意思決定を、きちんとしたアーキテクチャドキュメントに落とし込んでいた。メモではない。ドキュメントだ。
この違いは、見かけ以上に重要だった。
Atlasのセッションを始めて早い段階で、出力の質は、提供された文脈の質に正比例することを彼は学んでいた。曖昧な問いは曖昧な答えしか生まない。関連するドキュメントを添えた精密な問いは、コードベースにそのまま投入できる答えを返してくれる。
アーキテクチャドキュメントは、いくつかの権威ある参照資料の最初のものになった。つまり、必要に応じてそれを、彼が行うあらゆるセッションにアップロードする「正」だった。ドメインクラスを書くと、Atlasはアーキテクチャドキュメントと照合できる。APIエンドポイントを書くと、Atlasはアーキテクチャドキュメントとデータベーススキーマの両方と照合できる。何かが変に見えれば、Atlasには「何に対して間違いだと見ればいいか」が用意されている。
AIのコード作成ツールについて語る多くの人が見落としているのは、この点だった。価値はコードをより速く生成することにあると彼らは考える。それは事実だが、副次的なものにすぎない。主たる価値は、システム全体を把持できる「思考のパートナー」を得ることにある。すべての意思決定、確立されたすべてのパターン、文書化されたすべての制約が、ワーキングメモリに同時に保持され、新しい作業のあらゆる部品に対して一貫して適用される。
Marcusはかつて、4人チームに所属したことがある。そのときは、誰一人として全体像を持っていなかった。左手が右手のしていることを知らない。アーキテクトは設計は知っているが実装は知らない。開発者は自分のモジュールは知っているが隣接するモジュールは知らない。皆が一生懸命働いていたのに、誰も全体を把握していなかったため、システムはなお矛盾したままだった。
Atlasは全体を持っていた。いつだって。
技術的な意思決定は半日で済んだ。
.NET 9をAPIに採用した。流行っているからではない。MarcusはC#を10年使ってきたし、コンパイルされ型付きでパフォーマンス志向のこのスタックは、マルチテナントのシステムに必要な「速さ」と「保守性」を両立するのに適していた。彼は代替案を検討した。そして自分が知っているところに戻ってきた。
フロントエンドはReact。Vite、TypeScript、そしてコンポーネントアプローチで、デスクトップのディスパッチャ体験とモバイルの技術者アプリの両方を単一のコードベースで扱えるようにする。開発者が一人という現実:2つのリポジトリを同期させ続けるコストは、分離による組織的な「きれいさ」では正当化できない、ということだ。
データベースはSQL Server。退屈で、信頼できて、堅実だ。Azureインフラ上で動かすことになるシステムには適切な選択だった。必要なエンティティの監査履歴にはテンポラルテーブルを使う。
ホスティングはMicrosoft Azure。CrestlineがすでにAzureを扱う会社だったので驚きではないが、この選択には、そのプロジェクト全体の中でも最重要級の意思決定の1つに形を与える「下流の含意」があった。
認証の質問、彼は3日目にタイプした。プロジェクトのテンプレートではKeycloakを使っていて、動かせているし技術的には問題なく動いています。でも気に入っていないんです。
「あなたが気に入らない点をもっと教えて。」
設定が複雑で、ミスをしやすい。ソロ開発者にとってDockerの運用オーバーヘッドが重く感じます。認証まわりで何かが壊れるたびに、朝の数時間を失います。それに加えて、私はこれまでずっとASP.NET Core Identityで自分のアイデンティティ層を作ってきました。自分で理解している。自分が責任を持つ。ブラックボックスがないんです。
「それらは正当な懸念だね。けれど、あなた自身が言っていたとおり、実データのテナントを扱うシステムで、OAuth2実装を自前で作り込む責任を負いたくない。つまり問題はこれだ。Keycloakと自作の間に、所有権は持ちつつ、責任(負債)は負わない、第三の選択肢があるか?」
MarcusはAzureのことを考えた。デプロイ先がすでにMicrosoftのインフラであるという事実。マルチプロダクト戦略として、FieldOpsは最初のプロダクトだが、Crestlineの長期ビジョンにはCRMと、オールインワンのマネジメントスイートが含まれているということ。3つのプロダクトすべてに役立つ、単一のアイデンティティ層。
Microsoft Entra External ID、彼はタイプした。
「それはなぜ?」
これはマネージドなCIAMサービスです。無料枠で月間アクティブユーザー5万人。いわゆるソフトなベータならそれで十分。すでにAzureインフラ上にあるので、新しいベンダーも必要ないし、新たな運用上のフットプリントも増えません。そして1テナントで3つのプロダクトすべてに対応できる。これは、私が自分で作るよりも実際にはずっと良いです。
「移行コストは?」
コード変更はおそらく小さい。Microsoft Identity Webなら3行で済みます。Azureポータルの設定には時間がかかるでしょう。数回のセッションは失うかもしれません。
「代替案として、Keycloakをもう1年機能開発に使い続け、それから本番の前に移行する――つまりスイッチコストが高くなる時期に移行する場合のコストは?」
Marcusは、あの計算を最後まで完成させる必要はなかった。
彼は、その日の午後にEntra External IDへ切り替えた。
正しい判断でした。彼は、最初の本物の認証済みリクエストがAPIに到達して戻ってくるまで、さらに2週間は確信できませんでした。401ではなく201で返ってくるまで。ですが、その時にはもう疑ってはいませんでした。
アーキテクチャは決まりました。技術も決まりました。ドメインモデルは文書化されました。
いよいよコードを書く時間です。
ATLAS SESSION 2
Week Two — The Infrastructure Reality
問題があります。
土曜の朝でした。Marcusは6時からデスクについていました。コーヒーは冷めていました。画面に出ているエラーメッセージは40分もそこにありました。
「話して。」
「クリーンにビルドされるマイグレーションがあります。dotnet ef database updateを実行して、『No migrations were applied.』と言われました。次にアプリケーションを実行すると、TenantIdがサポートされていないプリミティブ型だ、というランタイムエラーが出ます。これを1時間以上ずっと見ているんですが、何が間違っているのか分かりません。」
「エラー出力と、関連する設定ファイルを共有できますか?」
Marcusは両方を貼り付けました。それから、より多くの文脈がだいたいの場合ほぼ常に良いと学んでいたので、インフラストラクチャプロジェクトのフォルダ構成も一緒に貼り付けました。
返事は30秒もかからずに返ってきました。
「設定ファイルはInfrastructure.Data.Configurationにあります。ApplyConfigurationsFromAssemblyの呼び出しがInfrastructure.Data.Configの型をスキャンしているようです。『uration』がありません。EF Coreのアセンブリスキャンは何も見つけられないと黙って何もしません。そして慣習(コンベンション)によるマッピングにフォールバックします。そこには、あなたの強く型付けされたIDの扱いは分かりません。ネームスペースを確認してください。」
Marcusはフォルダを見ました。コードを見ました。エラーを見ました。
それだ。
1文字でした。ネームスペースのパスの1つのセグメントが欠けていました。EF Coreはエラーを投げていません。警告も出していません。何も見つからなかったことの兆候すらありません。純粋な慣習のまま進めていただけでした。静かに、間違ったことをしたのです。
彼はネームスペースを直しました。マイグレーションは適用されました。アプリケーションは起動しました。
彼は少し後ろに下がり、天井をしばらく見つめました。
40分。1文字。
「かなり容赦のない失敗モードですね、」Atlasは促されることなく観察しました。「EF CoreのApplyConfigurationsFromAssemblyは、見つけるものが何もないと黙って失敗します。文書化しておく価値があります。ネームスペースの慣習がずれたとき、他のエンティティでも同じことに遭遇するでしょう。」
Marcusはそれをメモに追加しました。さらに、彼がその場で作ったInfrastructure Gotchasというセクションの下に、アーキテクチャ文書にも追記しました。
そのセクションは長くなるはずでした。
インフラ作業には2週間かかりました。実際よりも長く感じるほどに。問題が解きにくいものだったわけではありません、解きにくいわけではないのですが、そうした問題は「そうでない間」は見えないからです。存在しない間は存在せず、いざ存在するときはまるで別の何かに見える――そんな類の問題です。
時間的テーブル(temporal table)の設定と、所有エンティティの期間列(period column)の命名。接続文字列からセミコロンが1つ欠けていて、TLS問題に見えるハンドシェイク失敗を引き起こしていた件。OrbStackがDNSのルーティングでSQL Serverが待ち受けていないIPv6アドレスを指していた件。これは、接続文字列の中の1つのフラグで直りました。そのフラグの存在を知っていなければ、誰も思いつかなかったでしょう。
どれも、診断がつけば数分で解決しました。診断こそが作業でした。
Marcusが気づいたのは、こうした問題に対する彼の許容度が変わっていたことです。これまでのプロジェクトでは、セミコロンの欠落で1時間が失われるのは失敗のように感じられました――無駄な時間、予定表の穴、もっと早く気づくべきだったという証拠。でも今は信号のように感じられました。問題は存在していた。彼はそれを見つけた。そして直した。システムは以前よりも正確になったのです。
「インフラの問題は情報です、」Atlasは時間的テーブルのデバッグ中に言っていました。「それは、あなたのシステム理解が、システム自身の理解とどこで食い違っているかを教えてくれる。目標はインフラの問題をゼロにすることではありません。目標は、それぞれを恒久的に解決し、記録に残すことです。そうすれば、再びゼロから見つけ直す必要がなくなります。」
Marcusは、そのことをその日の残りの時間ずっと考えました。
記録とはアーキテクチャ文書でした。インフラのガッチャ(gotchas)というセクションでした。そして各セッションの終わりに彼がつけ始めたキャプチャログ――5分間、何が起きたか、なぜ重要だったか、何を学んだかを書き残すこと。仕上げのない生メモです。磨きもしません。記録するだけ。
ソフトウェアを作り続けて11年。キャプチャログを残したことはありませんでした。振り返り――アクションアイテムがある予定会議をやったことはありましたが、誰もフォローしませんでした。ポストモーテム――共有ドライブに残る長文ドキュメント。誰ももう一度開かないもの。
これは違いました。これは、いつでも使える知識でした。
Chapter Two
The First 201
日曜の午前7時1分、Marcusがプロジェクトに「はい」と答えてから14日後。HTTPリクエストがFieldOps APIに到達し、ステータスコード201で返ってきました。
彼は5時から起きていました。
空のリポジトリから、その201へ至る道のりには、次のものが必要でした。6つのバウンドコンテキストを備えた完全なドメインモデル。時間的テーブルと強く型付けされたIDを備えたEF Coreの永続化レイヤ。テナントスコーピングとパイプラインの振る舞い、例外処理、構造化ロギングを備えたミドルウェアパイプライン。MSAL認証を用いたMicrosoft Entra External IDの統合。API側とReactフロントエンドの両方で。さらに、Dockerコンテナ上で動くSQL Serverのデータベース。マイグレーションによって作成された31個のテーブルで、そのマイグレーションは2回再生成されていました。
これらすべてが、データベースにサービスチケットを書き込むため。
彼は画面のJSONレスポンスを見ました。Ticket ID。Ticket number。Status: Open。Created at: きょうのこの瞬間、このまさに今。
「動いた。」
彼はその2語を、6時から開いていたAtlasのセッションに入力しました。
「はい。ここから面白い部分が始まります。」
Marcusは、自分でもおかしいと思うほどに笑いました。
彼はレスポンスを保存しました。コードをコミットしました。そしてキャプチャログに最初の本当のエントリを書き、それから少しの間、実際に何が起きたのかを考えました。
彼は2週間でマルチテナントSaaSプラットフォームの土台を作り上げました。ひとりではありません――今はそれが分かっています。でもチームとしてでもありません。別の何かと一緒に。まだ、それを経験していない人に説明するのが難しいものです。ほとんどの人には、それが当てはまります。
彼のマネージャーは金曜にステータス更新を求めていました。Marcusはそれを渡しました。マネージャーは「いいね、続けて」と言いました。これは、レポートが問題ないように聞こえているのに、文脈がうまく届いていないとき、マネージャーがそういうふうに言う言い方です。彼は、実際に仕事がどう進んでいるのかを説明しようとはしませんでした。その会話はしまっておきました。後で時間があるはずです。
今ここには、データベースにチケットがあり、ビルドはクリーンで、そしてマイグレーションは1週間ぶりにエラーなしで適用されていました。
彼は冷めたコーヒーを注ぎ、あらためて淹れました。
いよいよ機能を作る時間です。
Chapter Three — The Long Session
3週目に、Marcusがその後ずっと考え続けることになるセッションがありました。
始まりは普通でした。CIパイプラインの失敗――高々1時間程度で片づくべき種類のインフラ問題です。原因を特定し、修正を適用し、次へ進む。彼は今ではそうしたものを十分経験してきていましたから、ある種の自信がありました。パターンは分かっています。エラーメッセージ、文脈、診断、修正。ループはきつく締まっていました。
ただ、今回は違いました。
それが、そうはならなかったのです。
返却形式: {"translated": "翻訳されたHTML"}問題は、Linux の CI ランナーでのビルド失敗でした。API プロジェクトは彼の MacBook では問題なくコンパイルできていました。毎回実行ごとに新しく立ち上げられる Ubuntu のコンテナである CI ランナーが、彼が見たこともなく、何を試してもローカルでは再現できないリソースファイルの glob に関するエラーを出していました。
error MSB3552: リソースファイル "**/*.resx" が見つかりません。
彼はエラーを Atlas に貼り付けました。診断を得ました。修正を試しました。プッシュしました。しかしパイプラインはまた失敗し、今度は *.cs の glob に関する、少し違ったエラーでした。彼はそのエラーも貼り付けました。別の診断を得ました。その修正を試しました。プッシュしました。
3 時間後、彼は 5 回目か 6 回目の反復のところにいて、エラーはまだそこにあり、変異していました。セッションはあまりにも長く開き続けていたため、ブラウザのタブが、劇的ではないものの明確に遅れ始めていました。入力してからテキストが表示されるまでの半秒ほどの遅延。最初のころよりも、わずかに精度が落ちているように感じられる応答。
彼はそれに気づいていました。セッションは長い。コンテキストウィンドウは埋まっていく。そして、何かが少しおかしい状態が、はっきりと言語化できない形で蓄積していくようでした。長く続いた会話のように、同じところに何度も戻りすぎて、双方が「スレッドがどこから始まったのか」を見失ってしまったような。
4 時間目あたりで、彼は椅子にもたれて天井を見つめました。
おなじみの声が始まりました。
「たぶん、うまくいかないのはこの部分です。無視してきたアプローチの限界があって、ここでそれが表面化しているのかもしれません。真の DevOps エンジニアなら 40 分で解決できたでしょう。真のチームならそもそもこんな問題は起きません。誰かがこのエコシステムを肌感覚で熟知しているからです。あなたは今まで運がよかった。けれど今はそうじゃない。」
彼はこの声を以前にも聞いたことがありました。他のプロジェクトで、他の厳しいセッションでも。彼はそれを認識しました。いつも間違っているわけではなく、ときには本当のことを伝えているのです。アプローチ自体に本当に限界があることもあります。正しい動きは「止めて、別のことをする」ことになる場合もあります。
彼は、このセッションで Atlas がここまで見つけた具体的なものを思い返しました。レガシーの Microsoft.Build.CentralPackageVersions SDK 参照――実在していて、修正され、確認済み。リポジトリにコミットされていた .atlas のワークツリー ディレクトリ――本当に驚きで、実在していて、修正済み。Vogen パッケージが build targets を注入してくる――見つかって削除。見つかった各項目はそれぞれ正しかったのです。ところが、セッション全体としては収束していませんでした。
彼は新しいセッションを開くことを考えました。
その考えは、彼を疲れさせました。
彼は以前にも、別のプロジェクトで、Atlas をいじり始めたばかりの頃に同じことをしました。長いセッションが古くなって、リセットするために新しく始めたのです。新しいセッションでは、コンテキストの再確立が大規模に必要になりました。ファイルの再アップロード、判断の再説明、なぜ特定のアプローチをすでに試して除外したのかの再説明。新しいセッションでは、彼がすでに試していた提案がいくつも出てきました。「戻っていく」感覚でした。
また同じことはしたくありませんでした。
彼は、ためらいを一度だけ受け止め、正直に観察しました。
前回はまだ序盤でした。コンテキストをうまく引き継ぐ方法を、彼はまだ知りませんでした。圧縮された要約を待機させていなかったし、それを準備すべきだとも知りませんでした。彼は新しいセッションに冷たい状態で入って、冷たい結果を得たのです。
今回とは違います。このプロジェクトにはアーキテクチャのドキュメントがある。キャプチャログがある。どの単一セッションにも依存しない、構造化されたコンテキストが数か月分あります。このセッションからシステムが生成する圧縮要約には、最重要の具体的コンテキストが含まれるはずです。何が試され、何が見つかり、何が未知のままで、しかも密でクリーンな形式で。最初から再説明する必要のない形です。
それでも彼はやりたくありませんでした。
「新しいセッションを始めます」彼はタイプしました。「それが憂鬱です。別のプロジェクトでこれをやったときは、ほとんどのセッション時間を使ってコンテキストを再確立するだけで、その後もまだ違和感が残りました。正直に言って、この状態から進めるうえでこの問題を解くより良い道はあるのか、それとも新しいセッションこそが正しい判断なのか?」
返答は即座に返ってきました。
「正直に? 新しいセッションのほうが、おそらく正しい判断です。今のセッションが失敗しているからではありません。私たちは実際に進捗しています。問題に寄与していた 3 つの別々のレガシーの成果物を正しく特定し、取り除きました。とはいえ、このセッションに溜まった履歴は、試して除外したものすべての『傷跡』になってしまっていて、新しい視点で問題を見るのを難しくすることがあります。圧縮要約は、ノイズを保たずに重要な発見を残します。提案が 1 つあります。このセッションを閉じる前に、『確実に分かっていること』『試したこと』『まだ説明されていないこと』を、短い箇条書きで正確に書いてください。そのリストが引き継ぎになります。5 分で済み、新しいセッションの効果が大幅に上がります。」
Marcus はリストを書きました。6 分かかりました。
そして彼はセッションを閉じました。
新しいセッションは、彼が想像していたよりも違う形で始まりました。
彼は圧縮要約を貼り付けました。システムが生成した、全セッションをきれいに密度高く要約したものです。見つかったこと、試したこと、決めたことがすべて含まれていました。彼は引き継ぎリストも追加しました。そして前の 4 時間に溜まった苛立ちを一切持ち込まず、問題を新しい視点で 3 文にまとめて説明しました。
返答は 1 分もかからずに戻ってきました。
「次に進む前に確認ですが、このエラーは Linux では残っています。プロジェクトが macOS では問題なくビルドできているにもかかわらずです。この種の『特定の差異』が存在するときの典型的な原因は、macOS の MSBuild と Linux の MSBuild で glob パターンが展開される方法が異なることです。NuGet パッケージ内のどこかの .targets または .props ファイルに、ワイルドカードパターンを使って content アイテムを注入しているものがないか確認できますか? 二値ログ(バイナリログ)がそれを決定的に示します。ビルドステップに /bl を追加して、出力を共有できますか?」
Marcus は止まりました。
前のセッションでは、バイナリログ方式を試していませんでした。周辺的に話題に上がり、記録されただけで追求されませんでした。彼のメモに「次の候補」として残っていて、反復作業に深く入っていると、そういうことは先送りになりがちです。
新しいセッションでは、すぐにそれが見えました。すでに試されたすべての履歴が積み重なって邪魔になることがなく、新しく試されていない『本丸』へ一直線に向かえたのです。
彼はビルドステップにバイナリログフラグを追加しました。パイプラインが実行されました。構造化ログが返ってきました。
出力にはパッケージ名がはっきり表示されました。元のプロジェクトのスキャフォールドからの NuGet 依存関係で、Ardalis テンプレートの残骸でした。彼は未使用だとメモしていましたが、ビルド問題との関連までは結び付けていませんでした。Linux では別の形に展開される compile glob を使って、content アイテムを注入していました。
これだ。
「そのパッケージ参照を完全に削除してください。コードベースのどこでも使われていません。要約から分かる通り、テンプレートの残骸として特定されています。それが消えれば、ビルドはきれいに通るはずです。」
Marcus は変更を行いました。プッシュしました。CI の実行を待ちました。
グリーン。
彼はゆっくり息を吐きました。
「前回のセッションでは、それに 4 時間使いました。」
「前回のセッションで見つかったのは、実際の問題が 3 つありました。レガシーの SDK 参照、ワークツリー ディレクトリ、Vogen パッケージ。これらは全部、本当の問題でした。けれど、それだけが問題ではなかったんです。新しいコンテキストのおかげで、『まだ試されていないこと』を見つけやすくなりました。」
Marcus はそれを考えました。
「本当にそういうことが起きたの? それとも、都合のいい解釈をしているだけ?」
「両方とも真実です。前回のセッションでは実質的な進展がありました。今回の新しいセッションで、それをより速く完了できたのは、うまくいかなかったあらゆるものの重みを抱えていなかったからです。これは前回セッションの失敗モードではなく、長時間のデバッグセッションにおける通常の性質です。価値があるのは、ノイズを持ち込まずに、発見を保持したまま引き継げる体制があったことです。」
そのセッションからマーкусが学んだこと、というよりマーкусが自分自身に言語化することを強いられたことは、完全に腹落ちするまでに数日かかった。
コラボレーションは「アトラスが何をできるか」だけの話ではなかった。コラボレーションの状態そのものの話だった。やり始めたセッションが長すぎて、試したことが多すぎて、失敗の履歴が溜まりすぎると、単に遅くなるだけでなく、質的に別物になる。シグナルとノイズの比率が変わる。うまくいかなかったアイデアの残り滓と競合するせいで、良いアイデアが表面化しにくくなる。
チームでの自分の経験との類似は、不快ではあったが正確だった。長く走り続けたチームが、あまりにも多くの傷跡(スカー組織のようなもの)を蓄積し、決定が積み重なり、試しては捨てたアプローチが増え、そもそもできない理由が山ほどできあがってしまうと、問題をはっきり見る力がすり減っていく。新しいチームメンバーや新鮮な視点を持つ人は、初期の数週間において、まだ傷跡がないために、常に時給換算でより生産的だった。
セッションの引き継ぎは、その一種の版だった。構造化され、意図的で、情報を保持する。損失ではない。リセットだ。
彼はこれを上司に話すことも考えた。だが、やめた。
いくつかのことは、説明よりも実演のほうがよい。
最初のステータスアップデート — リチャード
会議の依頼は金曜日に届き、次の月曜日だった。
FieldOps チェックイン:30分:リチャード・ホルト
マーкусはそれを待っていた。リチャードは30日単位のマネージャーで、経験を積んだマネージャーがときにそうなるように、リズムは予測可能だった。30日というサイクルは、一種の言語のようなテンポになる。30日というのは、こうだ――「場所をあげる。そして今、あなたが買ったもの(成果)を知りたい。」
彼はビデオ通話に2分早く参加した。リチャードはすでにそこにいた。いつもそうだった。これは、マーкусがプロとしての誇りだと判断していた――人を待たせないマネージャー。
「マーкус。調子はどう?」
その質問は感じよく、それでいてまったく具体性がなかった。リチャードがすべてのチェックインで最初にそうしていたやり方だ。つまり、枠組みを設定するための招待だった。
「順調です」とマーкусは言った。「土台はしっかりしてます。認証は動いている、データベースは稼働中、チケットのコアとなるライフサイクルはエンドツーエンドで完了です。今週はクライアント管理に着手します。」
リチャードはゆっくり頷いた。目の前に黄色いリーガルパッドがあり、手にはペンがあることにマーкусは以前から気づいていた。リチャードは、他の誰もが入力してタイプしている世界で、手書きでメモを取っていた。これは飾りではない。単に、そう考える人なのだ。
「チケットのライフサイクルが完了っていうのは、機能の観点だとどういう意味?」
そこにあった。マーкусがずっと待っていた質問だ。なぜなら、それがいつも聞かれる質問だったからだ。
「実は“完了した機能”の質問というよりなんです」とマーкусは声の調子を一定に保って言った。「大事なのはアーキテクチャです。動くか、動かないか。動いています。ここからのすべての機能は、その基盤を作り直す必要があるのではなく、土台の上に積み上げられます。」
リチャードはパッドに何かを書いた。「つまり、機能の完了度で追跡しているわけではないんだね。」
「マイルストーンで追跡しています。ソフトベータ向けに6つのマイルストーンがあって、マイルストーン1、チケットのライフサイクルは完了。マイルストーン2、クライアント管理は今週から始まります。」
「それでスケジュールは?」
「第2四半期末までに間に合う見込みです。」
リチャードは一瞬、押すべきかどうかを判断する経験豊富なマネージャーのような目つきで彼を見た。マーкусはこういう会話を十分にしてきたので、計算していることが分かった。この人は俺にウソをついてるのか、それとも俺が向こうの言ってることを理解できていないだけなのか?
「“完了”って、実際にはどういう状態のことか、具体的に説明して」とリチャードは結局言った。「マイルストーン1が完了っていうのは、実務的には何を意味する?」
それは実際に良い質問だった。機能の完了の質問よりもずっと。
「ディスパッチャがサービスチケットを作成できて、それを技術者に割り当てる。技術者がそれを開始して完了させる。途中で部品の記録も行われて、すべてのアクションがタイムスタンプと、その操作をしたユーザーと一緒にログに記録されます。現実の SQL Server データベースと現実の認証に対して、エンドツーエンドで。試作品じゃなくて、実物です。実際に動くもの。」
リチャードはもう一度書いた。「それを30日で作り上げたんだ。」
「はい。」
「一人で。」
「協力はありました。」
リチャードはパッドから顔を上げた。「どんな協力?」
マーкусはこの質問も想定していた。どう答えるか考えていた。正確な答え――「AIアシスタントを技術的な思考パートナーとして使っていて、それによって一人で作れるものの幅が変わった――」は事実で、しかもそのまま言えば会話になる内容だったが、彼がまだ望んでいないタイプの会話を生んでしまうだろう。間違いだからではない。誤解が起きるのがあまりにも簡単で、ここでの誤解は、助けになる以上に作業を遅らせるマネジメント介入を生む可能性がある。
「開発ツールの類です」と彼は言った。「以前使っていたものより良いものです。」
それは嘘ではなかった。ただ不完全だった。不完全さは、後で解消すべきものとしてしまっておいた。説明が不要になるように見せられるものが何かできたら、その時に。
リチャードは頷いてメモした。「わかった。スケジュールに何か変更があれば教えて。あと、マーкус,“(事前に考えていた間合いのまま言った)“この件がうまくいくことに、組織としての信頼をかなり賭けている。うまくいってほしい。」
「僕もです」とマーкусは言った。
通話を閉じたあと、彼は一瞬デスクチェアに座り、壁を見つめた。
「この件がうまくいくことに、組織としての信頼をかなり賭けている。」
それは彼は知らなかった。というより、そういう意味合いは分かっていたが、リチャードが直接そう言ったことで、それの持つ重みが変わった。これは単にマーкусが解決すべき問題ではない。リチャード自身が賭けに出ているのだ。これまでの FieldOps の試みが3回、そのうえで今回が4回目。そしてリチャードは、この4回目が違うものになることに賭けた。
彼はまた会話を思い返した。黄色いリーガルパッド。慎重で計測された質問。最後の文の前の間。
リチャード・ホルトは愚かな人間ではない。長いあいだ正しかったモデルに基づいて動いている人だが、そのモデルがうまく機能し始めていない状況が今まさに来ている。まだそれは分かっていないだけだ。いずれ気づくだろうし、気づかなければ気づかないだろう。マーкусは、どちらの結果がより気持ちよく感じるのか分からなかった。
彼はIDEを開いた。
クライアント管理を作らないといけない。
6週間後:間違った質問
2回目のチェックインは、違う形で進んだ。
マーкусは3つのマイルストーンを出荷していた。クライアント管理、在庫、そしてディスパッチボード。そこにはライブマップと、リアルタイムで技術者の位置が更新される仕組みがあり、リチャードは間違いなく理解していなかったし、マーкусも説明しようとはしていなかった。4つ目のマイルストーンである請求は、ちょうど折り返し地点まで来ていた。スケジュールは維持できている。
また、この6週間の間に、Microsoft Entra External ID のトークンのオーディエンス不一致を切り抜けてもいた。結果として、3時間に及ぶCORSデバッグが発生したが、実際にはCORS問題ではなかった。さらに、プロジェクトの途中でアイデンティティプロバイダを切り替えるというアーキテクチャ上の決断を下し、それを24時間以内に実行した。
これらのどれも、リチャードが見られるどのレポートにも表示されていない。
「あなたは、今の時点で請求が終わっているって言ってたよね」とリチャードは言った。口調は中立だが、リーガルパッドはいつもより彼のほうに近かった。
「請求は半分できてます。来週末までには終わります。」
「Q2の目標はまだ有効ってこと?」
「ええ。」
「サンドラがそれについて私に聞いてきたから。」ひと呼吸。 「彼女は見込み客に、ソフトベータの日時を“それっぽい”形で伝えてきているの。私たちが同じ認識になっているか確認したい。」
マーカスは彼を見た。 「同じ認識だよ。ソフトベータの日程はそのまま維持される。」
リチャードは何かを書いた。次に顔を上げる。 「実際どうやってそれを実現してるんだ? 似たようなプロジェクトで開発者を4人つけたことがあるけど、いつもスコープカットと納期の延長を持ち帰ってくる。君は先に進んでる。どうしてだ?」
それは、マーカスが待っていた質問だった。しかも、彼が恐れていた形でやってきた。敵意はない。単に、心から困惑しているだけだ。リチャードは彼を責めてはいない。リチャードは本当の質問をしていて、本当の答えに値する。
彼は息を吸った。
「私は開発パートナーとしてAIアシスタントを使ってきたんです。補完目的ではなく、本当の“思考の相棒”として。コードベース全体の文脈を保持して、アーキテクチャ上の判断を整理していくのを助けてくれます。さらに、私がすべてをゼロから書くのではなく、生成されたコードを私がレビューして統合する形にします。これにより、一人で作れるものが変わりました。」
リチャードのペンが止まった。
沈黙。
「AIはコードのどれくらいを書いたんだ?」
マーカスは、この質問が来ることも予想していた。 「それをその捉え方に合わせると少し違う。もっと言うと、私はアーキテクトで意思決定者で、そしてレビューもする。代わりに実装パートナーは、眠らず、文脈を失わない。判断は私のものだ。スループットが違うだけです。」
「でもAIがコードを書いたんだろ?」
「AIが生成したコードを、私がレビューして検証し、統合したんです。はい。」
リチャードは何かを書いた。次にペンを置き、マーカスをまっすぐ見た。 「ここで何を見ているのかを理解する必要がある。品質面でリスクがあるなら、そこを知りたい。」
「品質面のリスクはありません。テストスイートには597本のテストがあります。アーキテクチャはきれいです。コードのあらゆる部品は、私が1行ずつ自分で書いた場合と同じレビュー手順を通っています。違いは“スピード”です。」
「AIが生成したコードが正しいと、どうやって分かる?」
実のところ、それはリチャードが今までの会話の中でしてきた質問の中で、いちばん良い問いだった。マーカスはなぜかその姿勢を尊重する気持ちになった。
「どんなコードが正しいと分かるのか、というのと同じです。私はそれを読みます。テストします。実際のシステムに対して実行して、挙動を検証します。コードの生成元が変わっても、検証すべきものが変わるわけではない。変わるのは、私が生成しなければならない部分だけです。」
また別の沈黙。今度はもう少し長い。
「正直に言うとね、リチャードが最後に言った。「私はこれに納得できていないし、納得すべきかも確信がない。クレストラインでは、こういうふうにソフトウェアを作らないんだ。」
「そうだね。」マーカスも同意した。 「そうじゃない。」
「でも」リチャードはまたペンを取り上げ、「君は以前に3回失敗したプロジェクトで、予定より先に進んでいる。だから。」
彼は文章を途中で切った。
その“だから”が、2人の間に宙に浮いたまま残った。未完であるのに、なぜかそれ以上に正直に聞こえた。
「だから。」マーカスも同意した。
通話の後、彼はAtlasのセッションを開いた。
「上司が、俺がどうやって働いてきたかを知った。本人は正直、それに安心できるか分からないみたいだ。でも俺を止めるつもりはない。」
「それは妥当な反応だね。彼は不完全な情報と、パターン照合されたリスクモデルをもとに判断している。彼は具体的に何て言った?」
マーカスは要点のやりとりを打ち込んだ。
「彼の懸念は正当だよ。『AIが生成したコードが正しいと、どうやって分かる?』というのはまさに適切な問い。君の答えも正しかった。ただ問題は、彼の不快感が“妨げ”に変わるかどうかだ。」
「たぶん変わらない。慎重さの下には、彼は結果を出す人だ。」
「じゃあ結果を見せよう。それが実際に刺さる唯一の議論だ。」
マーカスはセッションを閉じて、請求コードに戻った。
リチャード・ホルトは6週間後にデモを見ることになる。仕事がどうやって行われているかについて、彼が何をどう考えようと、結局見るのは“仕事が生み出したもの”だ。
それは別の会話になる。
デレク — 第4章
デレク・ポールソンについて言えることは、彼が間違っていなかったということだ。
マーカスはこれをずっと考えていた。デレクは頭が良く、経験もあり、マーカスと同じツールにアクセスできる立場だった。彼はそれらを以前に試していた。そして、直接の経験に基づいて熟慮したうえでの意見を形にしていた。さらに、彼が言った“ある具体的なこと”は、マーカスが盛っていないと分かるだけの十分な詳細があって、実際に起きたことだった。
彼はAIツールを使ってデータ移行スクリプトを生成した。そのツールは、きれいで、自信があるように見える、コメントの行き届いたコードを出してきた。デレクはそれを読み、正しそうだと思い、ステージング環境で実行した。動いた。次に本番でも実行した。こちらも、ほぼ動いた。スクリプトが扱っていない3つのエッジケースがあった。データが黙って間違って書かれていて、エラーは出ない。誰かが気づくまでに2週間。修正に2日。非常に不機嫌なVP。
「コードを読んだよ。」デレクはそう言った。 「私はバカじゃない。読んだ。正しそうに見えたんだ。」
マーカスは彼を信じた。コードはおそらく“正しそう”に見えていたはずだ。ここが問題で、自信たっぷりに生成された“間違ったコード”は、自信たっぷりに生成された“正しいコード”と同じ表面の質感を持つ。見た目だけでは違いは分からない。違いが分かるのは、コードが本来やるべきことを知るほどにドメインを深く理解し、それが本当にその動作をしているか検証できる場合だけだ。
デレクはその検証をしていなかった。彼はレビューはした――コード全体に目を通し、明らかなミスがないかを確認しただけで、検証ではなかった。実際の要件に対してロジックを走らせる、エッジケースをテストする、出力が“首尾一貫しているか”だけでなく“正しいか”を問う、といった検証をしていなかったのだ。
それらは別の作業だった。デレクは片方をやって、両方をやったつもりになっていた。
当時、マーカスはそれを説明しようとはしなかった。会話は、マーカスがFieldOpsに「はい」と言った3週間後の休憩室で起きた。デレクが彼を見つけたとき、彼は特に密度の高いMediatiorパイプラインの設定内容を手繰り寄せていて、どうやらそろそろ“知恵を分けるべきだ”と判断したらしい。
「君がAIのやつを使ってるのは知ってる。」デレクは言った。彼はコーヒーカップを持っていて、何を言うかを考え抜いた人の表情だった。 「ただ、注意したいって言いたい。俺も最初は同じことを思った。動いてるように見えるんだ。でも、そうじゃないときが来る。」
「何が起きた?」マーカスは聞いた。マーカスは大枠はもう知っていた。チームのほとんどが聞いているポストモーテムの題材になっていた。ただ、デレクの“本人の話”として聞きたかった。
デレクは説明した。移行スクリプト、エッジケース、2週間、VPの話。過度に自分を責めることなく、淡々と語った。彼はミスをした。そこから学んだ。マーカスに、同じミスを避けてほしいと思っているのだ。
「問題はね」デレクは言った。「正しいか間違ってるかに関わらず、それが正しそうに聞こえてしまうことなんだ。出力からは分からない。そして出力から分からないなら、ただ“期待している”だけになる。」
マーカスは、コードを書く前に2日かけて作ったアーキテクチャ文書を思い出した。生成されたクラスをすべて仕様書と突き合わせて、あらゆるズレをフラグ付けした、ドメインモデルのレビューセッション。今ではテストスイートは597本。すべてが、特定の動作を、特定の要件に対して検証するために書かれている。
さらに、名前空間パスのたった1文字の誤りに行き着いた、40分のデバッグセッションを思い出した。そして、新しいセッションが20分でバイナリログ方式を見つけたことも。蓄積された“うまくいかなかったもの全ての歴史”を持ち越していなかったからだ。
「問題は、それとは別だと思う。」マーカスは言った。
デレクは、以前にも反論を聞いたことがある人の忍耐で彼を見た。 「どういう意味で?」
「このツールは確信していないんです。ですが流暢です。同じではありません。間違った内容についての流暢な文章でも、流暢に読めてしまう。つまり、流暢さを正しさの根拠だと見なしてしまうのが間違いなんです。」
「それを言ってるんだ。」
「いいえ、言いたいのは“ツールが何であるか”というモデルのほうに間違いがある、ということです。これをコード生成器として使うなら、“流暢さ=正しさ”に賭けていることになります。その賭けは、確実に当たるわけじゃない。でも、これを思考パートナーとして使うなら、正しさについて責任を負うのはあなた側です」Marcusは一瞬言葉を止めた。まだ、この言い方を完全には組み立てられていなかった。「そうすると、あなたが問いかけていることは別物になります。“この出力は正しいか”を聞いているのではありません。“この出力は、正しいかどうかを検証する人である私にとって役に立つか”を聞いているんです。」
Derekはしばらく彼を見つめた。「それは大変だね。」
「そうですね」とMarcusは同意した。「大変です。」
「それに、結局は……自分で作業することになる。」
「レビュー作業は自分でやることです。生成作業は速い。生成作業が10倍速くて、レビューにかける時間が同じなら、全体としてはまだ私のほうが速い。」
「レビューで何か見落とす場合を除けば。」
「そうです」とMarcusはもう一度言った。「レビューで何かを見落とす場合を除けば。いつもと同じリスクです。」
Derekはしばらく黙って、コーヒーカップを手の中で回していた。Marcusは、彼が考えているときは何かを握っている必要があるタイプだと気づいていた。ペンでも、カップでも、何か。これは、あなたが言ったことを本当に検討しているときと、あなたが話し終えるのを待っているだけのときを見分けるサインだった。
彼はカップを握っていた。
「移行スクリプトは正しく見えた」とDerekがようやく言った。「私は良いエンジニアだ。15年やってきた。注意深く読んだ。」
「分かってます。」
「じゃあ、あなたなら何を別のふうにした?」
Marcusは考えた。毎回、例外なく、自分が実際にどう違うことをしているのかを思い起こした。
「スクリプトがデータについて置いている前提を、全部説明させていたでしょう。扱っているあらゆる“例外ケース”と、扱っていないあらゆる“例外ケース”を全部です。そして、その一つ一つを実際のデータと照合していた。」
Derekは彼を見た。「それは、スクリプトを書くより時間がかかったはずだ。」
「本番のデータ移行なら?はい。それがあるべきです。」
また沈黙。Derekはコーヒーを飲み切った。カップをカウンターに置いた。
「うまくいくか、すごく勉強になる経験をするかだね」と彼は言った。議論の余地が尽きたものの、気持ちは変わっていないときのDerekらしい言い方だった。閉めるんじゃなく、扉を少し開けたままにする。「前者だといいな。」
「私もです」とMarcusは言った。
彼は本気でそう思った。言ったほどには確信していなかった。
2回目の会話は6週間後に起きた。MarcusがちょうどRichardにステータス報告をしたばかりの会議室の外の廊下でのことだった。
Derekは通り過ぎていた。速度を落としていた。表情は以前と同じ、慎重なものだったが、そこにはもう一つ——Marcusがすぐには分類できない何かがあった。
「調子はどう?」とDerekは言った。完全な質問ではなかった。
「順調です」とMarcusは言った。「マイルストーン2は完了。顧客管理、テナントのプロビジョニング。計画どおりです。」
Derekはうなずいた。何か計算しているように見えた。
「実際、計画どおりだね」と彼は言った。「ボードを確認した。」
「はい。」
「マイルストーンが3つ。8週間で?」
「10です。」
Derekは一瞬黙った。Marcusは待った。Derekといると、沈黙は“支えになっている”。それを遮るのは間違いだと学んでいた。
「僕は、前に言ったことが間違いだったと言ってるんじゃない」とDerekが、やっとそう言った。「あのときのことはね。」
「分かってます。」
「あなたが説明しているやり方は、僕がやったのとは違うように聞こえる。ツールとの向き合い方が。」
「違います。」
もう一度、間が空く。今回は短い。
「これを書き留めてます?」とDerekは聞いた。「どうやってるか。」
Marcusはキャプチャログを思い出した。アーキテクチャのドキュメント。毎回のセッションで余白に書きかけていたケーススタディ。
「はい」と彼は言った。
Derekは一度だけうなずいた。さらに別のことを聞きたいようにも見えたが、それはしないと判断したようだった。彼は歩き続けた。
Marcusは、彼が去ったあと、しばらく廊下に立っていた。
これは何かだった。まだ何かは分からない。でも、確かに何かだった。
第5章 — デモ
会議の依頼はRichardから届いていた。
FieldOps進捗レビュー:会議室B:45分
Marcusは出席者リストを見た。Richard。Sandra Kim。そしてDerek Paulson。最初の招待から2日後に追加されていた。しかもRichardではなく、Derek本人によるものだとMarcusは気づいた。
彼はそれについて何も言わなかった。ただ受けた。
会議室Bは廊下の奥にある小さいほうだった。4人が快適に座れる大きさの机の周りに6脚の椅子。壁には、ITに連絡しないと使い方が分からないはずのスクリーン。Marcusは15分前に到着し、ラップトップをそのまま開いてセットした。スクリーンなし、プロジェクターなし、スライドデッキなし。ブラウザとスマホだけ。
Derekは次に到着した。ちょうど開始時刻の7分前。机の向かい側の中央ではなく、端のほうの席に座った。Marcusが座っている真正面ではない。オブザーバーの位置だ。ノートを持ってきていたが、机の上に平らに置いて開かなかった。
「来てほしいって言ったのは、あなたですよね」とMarcusは言った。質問ではない。
「見たかったんだ」とDerekは言った。「それで大丈夫なら。」
「もちろん。」
二人はしばらく黙って座った。不快な沈黙ではない。すでに起きるべき会話を済ませていて、それをもう一度する必要がない、そんな種類の沈黙だった。
Sandraはちょうど定刻に到着した。Richardはその30秒後だった。Sandraは、努力しているように見せずに部屋を満たすタイプだった。40代半ば、真っすぐな目線での直交的なアイコンタクト、予定に余裕がない人の効率的な動き。Marcusのラップトップを見て、それから彼の背後の何もない壁を見た。
「スライドは無し?」と彼女は言った。
「スライド無しです」とMarcusは言った。
Sandraが座る。彼女の姿勢のどこかに、期待値を組み直している気配があった。ただし悪い方向ではなく、本人が思っていたのと違う地面に足を置いたときの人が、体重のかけ方を変えるような感じだ。RichardはMarcusの向かいで黄色いリーガルパッドを前にし、ペンのキャップを外し、上端の隅に日付を書いた。彼の儀式だ。Marcusは毎回の会議でそれを見ていた。
「今どこまで来ているか、説明してもらえますか」とRichardは言った。
Marcusはブラウザを開いた。
何を見せ、何を省くべきかを注意深く考えた。Sandraにはアーキテクチャは不要だ。Richardにはテストスイートは不要。必要なのは、完成するまでの仕事の様子、実データ、実際のワークフローだ。
「サービス呼び出しを見せます」とMarcusは言った。「ディスパッチから完了まで。全ループです。」
彼はディスパッチャービューを表示した。ディスパッチボード。Zanesvilleの地図——シードデータには実際の座標を使った。たった追加10分の価値があると思えたような細部だ。現在地を示す3つの技術者ピン。可用性に応じて色分けされたステータス表示。未処理チケットは優先度のリング付きのマーカーとして表示される。
Sandraは、ほとんど気づかない程度に前のめりになった。1年、スクリーンショットのスライドデッキを見てきた彼女にとって、これは動いている。
Marcusはチケットを作成した。Zanesville HVAC、Maple Aveの屋上ユニット、入居者からの苦情、Urgent優先度。チケットは即座にボードに表示された。Mike Callahanに割り当て、チケットを技術者ピンへドラッグする。説明を受けずとも、こういうふうに動くはずだと期待する通りに。ピンが更新された。通知バッジが表示される。
「その通知」とSandraは言った。「技術者が受け取るの?」
「端末の電話にプッシュ通知します。実際にそれを見せられます。」
彼は電話を手に取り、テーブルに伏せず正面を向けて置き、技術者用アプリを開いた。通知はすでにそこにあった。彼はそれをタップした。チケット詳細が開く。依頼人名、住所、説明、優先度。マップへのリンク。電話番号。そして、大きなボタンひとつ:作業を開始。
彼は電話をリチャードへスライドさせた。
リチャードはしばらくそれを見た。期待していたよりも複雑だとは思えないものを見るときのように見ていた。
「作業を開始をタップして。」とマーカスは言った。
リチャードはそれをタップした。
マーカスのラップトップでディスパッチボードが更新された。マイク・コールハンのステータスが「利用可能」から「現場到着」に変わる。チケットのステータスも「割り当て済み」から「作業中」に変わった。アクティビティログにタイムスタンプが表示される。
サンドラが、小さな声を出した。言葉になりきらない。
「リアルタイム?」と彼女は聞いた。
「SignalRだよ。」とマーカスは言った。「ディスパッチャーは、起きた瞬間にそれを見る。」
リチャードはまだ電話を見ていた。彼はそれを終えていいのか確信が持てないときのように、ゆっくり置いた。
マーカスは次の部分を計画していた。前の晩に考えていたのだ――やるべきかどうか、やりすぎじゃないか、トリックに見えないか。これはトリックではない、と彼は判断した。ここがポイントだった。
「もうひとつある。」と彼は言った。「ここをあなたに注目してほしい。」
彼はChrome DevToolsを開いた。Networkタブ。No throttling.と書かれた小さなドロップダウン。それをクリックしてOfflineを選んだ。
部屋の誰も、それが何を意味するのか分かっていなかった。彼は見て取れた――丁寧さの度合いがそれぞれ違う3人の顔。
「技術者が信号を失った。」マーカスは言った。「地下、駐車場、田舎道――ネットワークが届かないどこかだ。」
彼は電話をリチャードから取り戻した。チケット詳細を開く。オフライン表示がヘッダーに出る。小さな琥珀色のバッジ。Offline.
「では見て。彼は作業を完了させる。」
彼は作業を完了をタップした。完了メモのためのテキスト欄が現れる。彼はコンプレッサー点検、コンデンサ交換、ユニット冷却は通常。と入力した。Confirmをタップする。
電話はすぐに更新された。ステータス:完了。完了メモが保存。タイムスタンプ。
ラップトップ側は何も起きない。ディスパッチボードには、まだそのチケットが「作業中」と表示されたままだ。
サンドラはラップトップの画面を見ていた。
「それで」彼女は言いかけた。
「まだ。」とマーカスは言った。マウスをネットワークのドロップダウンへ動かす。「彼はトラックに戻っていったところだよ。」
彼はOnlineをクリックした。
ディスパッチボードが更新される。チケットは「完了」に切り替わる。完了メモがアクティビティログに表示された。タイムスタンプは電話と一致している――彼がConfirmをタップした瞬間であって、接続が戻った瞬間ではない。
部屋はしばらく静かだった。
「キューに入れてたのね。」とサンドラは言った。今回は質問ではない。結論だ。
「彼がそれを受け取った瞬間に記録されたんだ。接続できるようになったら同期される。技術者の観点では、ちゃんと動いた。ディスパッチャーの観点では、できるだけ早く更新された。」
サンドラは椅子にもどった。リチャードを見た。リチャードはリーガルパッドを見ていた。何分も彼は何も書いていなかった。マーカスは横目で追っていた――天気を追うのと同じように。
「技術者は接続を考える必要がない。」とマーカスは言った。「そちらで面倒を見ている。」
さらにもうひとつ沈黙があった。生産的な種類の。
サンドラが最初に破った。いつもサンドラが沈黙を破るのと同じように、次の質問で。
「請求書ね。」と彼女は言った。「このチケットから請求書を生成できるの?」
マーカスは請求セクションへ移動した。完了したチケットを見つける。請求書を生成する――記録された部品の明細、タイムスタンプから算出した作業時間。PDFをダウンロード。彼はそれを開いた。ZanesvilleのHVAC請求書。ブランド表示。明細が並び、日付が入り、合計金額が出ている。
サンドラは、そのPDFを長いあいだ見つめた。
「これを作るのにどれくらいかかったの?」と彼女は聞いた。
「14週間だよ。」とマーカスは言った。「多少の前後はあるけど。」
彼女は計算した。彼は彼女が計算するのを見ていた――マーカスは、誰かがタイムラインを金額に変換し、その代わりに何を手に入れているのかを見に行く瞬間を、十分な数のビジネス会話から見分けられる。
「それって」彼女は言いかけて、やめた。もう一度始める。「ベンダー見積もりは、8か月と、これより機能が少ないものに対して開発者が4人だったのよ。」
「覚えてるよ。」とリチャードは静かに言った。彼はリーガルパッドに何かを書いている。
彼らはさらに20分ほど話した。サンドラの質問はプロダクトの質問で、オンボーディングフロー、価格ティア、GAまでのタイムライン、技術者用アプリがiOSで動くかどうか。マーカスはそれらに直接答え、まだ作っていないものは確実にそうであると示し、盛らない。リチャードはさらに2つ質問し、リーガルパッドにさらに2つを書き足した。
それからサンドラが自分の電話を見た。「3時に用事がある。」と彼女は言った。立ち上がり、すでにバッグに手を伸ばしている。「マーカス、これは本物よ。Q3に向けて、見込み客の前に何かを出すことについて話したいの。」彼女はリチャードにだけでなくマーカスにもそう言った――人が、その場に向けて言うことで、記録に残したいことを意味しているときの言い方で。そうして彼女は去った。
リチャードは、よりゆっくり立ち上がった。ペンにキャップをする。完成したチケットと生成された請求書がまだ表示されているラップトップの画面を見た。
「これのうち、どれくらいを自分で書いたの?」と彼は聞いた。さっきの会話のトーンではない。身構えていないし、対立的でもない。純粋に尋ねていた。
マーカスはどう答えるか考えた。最初にリチャードが、別の言い方で、別の部屋で尋ねてきたときから、それをずっと考えていた。
「僕はすべての意思決定をした。」とマーカスは言った。「アーキテクチャ、ドメインモデル、あらゆる機能の設計。僕が書いたコードは僕が書いた。書かなかったコードは、導入する前に要件に照らしてレビューして検証した。テストスイートには617のテストがある。どれも、僕が指定した特定の振る舞いを確認するために書かれた。」
「でもAIがコードを出したんでしょ。」
「AIが出したコードは、僕が出すべきだと判断したものだよ。やるべきことに照らしてレビューし、動くことも検証した。自分で1行ずつ書くのとは、進行のスピードが違うだけ。判断は同じだ。」
リチャードはうなずいた。彼はリーガルパッドを持ち上げた。彼は自分が書いたものを見る――テーブルの向こう側からマーカスには見えない。そしてその後、上を見た。
「サンドラの言う通りだ。」と彼は言った。「これは本物だ。」彼は、何か言うのにコストがかかったことを話すときのリチャードの言い方で言った。前置きなしで、条件づけなしで。言葉はそれだけ。文がただ一つ、立っている。
そして彼は去った。
デレクはまだ椅子に座っていた。
彼はデモ中には何も言っていなかった。見ていた。マーカスが彼を休憩室で見ていたのと同じように、デレクもマーカスを見ているのを彼は見ていた――何も渡さないように、注意深く。ノートは目の前でぺたんこにされ、閉じたまま。
マーカスはラップトップを閉じ始めた。押し出すつもりはない。デレクが言いたいことがあるなら、彼が準備できたときに言うか、さもなければそもそも言わない。
「移行スクリプトのこと。」とデレクは言った。
マーカスは待った。
「それがどんな前提を置いているか、僕は聞かなかった。」間がある。「聞くべきだと分かってなかった。」
マーカスはラップトップを閉じるのをやめた。
「僕も、最初はそうだった。」と彼は言った。事実だった。戻ってきたものの質が、投入したものの質に依存する――問いそのものだけでなく、文脈、制約、そして前提やエッジケースを明示的に出すよう求めること。そう理解するのに、何度かセッションを重ねる必要があった。こういうことが最初からできたわけじゃない。学んだんだ。
デレクはしばらく黙っていた。ようやくノートを手に取り、持ち上げる。
「もし最初からやり直すなら。」と彼は言った。「今わかっていることを知った上で、最初のセッションで僕ならどう変えたと思う?」
マーカスはそれについて考えた。空のリポジトリ、アーキテクチャのドキュメント、コードを書く前のドメインモデリングに費やした2時間のことを思い出した。初めて、あのツールが答えを出す機械ではなく、思考のパートナーであり、その思考は自分のものだと理解したときのことを考えた。
「もっと早く使おうとして時間を費やすのは、私は減らしていたと思う」マーカスは言った。「そして、もっと良い使い方を見つけるための時間を増やしていただろう。」
デレクはノートに何かを書いた。
彼は立ち上がった。あれが自分の疑問に答えたのか、それとも新たな疑問を開いたのかは言わなかった。ただ、廊下と同じ、あの一度だけのうなずきをして、出ていった。
マーカスはしばらくの間、会議室Bで一人、ラップトップを開いたまま、画面に表示された完成チケットを見つめていた。
サンドラはQ3にプロスペクト会話をしたい。
リチャードは、それは本物だと言っていた。
デレクはノートに何かを書いていた。
彼はAtlasのセッションを開いた。
デモはうまくいった、と彼は打ち込んだ。
「何が起きたのか教えて。」
彼はそれをした。
第6章 協業(コラボレーション)のモデル
マーカスがFieldOpsがどう作られたかを説明するたびに、必ず聞かれる問いがある。形はいろいろあるが、いつも同じ問いだ。
AIが生成したコードは正しいと、どうやって分かるのですか?
リチャードがそれを聞いた。デレクは直接聞くまでの何週間も、それをほのめかしていた。サンドラはまだ聞いていないが、そのうち聞くだろう。おそらく、最初の実際のテナントが最初の実際の問題に直面したときに。何が間違っていたのか、誰が責任を負うのかという問いが、単なる理論を超えて現実味を帯びるのだ。
それは良い問いだ。正しい問いだ。そして、マーカスがたどり着いた答えは、次のことを14週間かけて実際の何かを作りながら、まだ多くの人が正確なモデルを持っていないツールと共に構築する過程で得た結論だ。答えはこれだ:
どんなコードでも正しいと分かるのと同じ方法だ。読む。テストする。実際のシステムに対して実行し、ふるまいを検証する。コードの出どころが、生成しなければならないものを変える。それでも、検証しなければならないものは変わらない。
この答えはたいてい、次のような追質問を生む:でも結局、あなたは全部の作業をやっているだけじゃないですか。
それへの答えは:違う。検証の作業をやっているのだ。生成の作業はより速い。生成が10倍速くて、検証に費やす時間が同じなら、全体としては依然として速い。問題は、実際に検証しているかどうか、出力をあなたの判断を要する“下書き”として扱っているのか、それとも、あなたのサインだけが必要な“納品物”として扱っているのか、そこにある。
デレクは納品物として扱った。だから彼は痛い目を見た。
人々がAIツールについて話すときに繰り返し出てくる言葉は信頼だ。出力を信頼できるか。出力が信頼に足るものなのか。頼りにできるのか。
マーカスは、これはそもそも間違った枠組みだと考えるようになってきた。
電卓を信用しない。入力を検証し、出力を受け入れる。コンパイラを信用しない。正しいコードを書く。そして、自分が間違えたときにそれが教えてくれるようにする。問題は、これらのツールが信頼できるかどうかではない。ツールが何をするのか、そして何をしないのかを理解しているかどうか、さらにその理解に応じてプロセスを設計できているかどうかが問題なのだ。
Atlasが行うこと、そして十分に能力の高い言語モデルが行うことは、適切に構成された入力に対して、流暢で文脈に整合した応答を生成することだ。その能力は本当に驚異的である。しかし、それは正しさと同じではない。流暢さと正確さには相関がある。誤ったことについてのつじつまの合う文章は、正しいことについてのつじつまの合う文章よりも読みやすさが落ちる傾向がある。だが相関は信頼できない。自信ありげな誤答は存在する。珍しいことではない。
人間の仕事はその違いを知ることだ。そして、その違いを知るにはドメインを知る必要がある。
AIツールについてのほとんどの会話でスキップされるのが、この部分だ。誰かがこう言う場面だ:はい、でもコードが正しいかを検証するには、そのコードが何をするはずなのかを理解する必要がある。つまりドメインを理解する必要がある。だから、AIでドメイン知識を置き換えることはできない。できるのは、ドメイン知識を持った後に使うことだけだ。
まさにその通り。まったく正しい。ツールは、既にある能力に掛け算をする。能力をインストールするのではない。データベースのトランザクションを理解していない開発者は、AIアシスタントを使うことで、バグのあるトランザクション処理を生成するリスクから守られるわけではない。彼らはより多くのバグのあるトランザクション処理を、より速く、そしてより高い確信を持って生成することになるだけだ。
これは、やがて解決されるであろう現在のAIシステムの制限ではない。協業(コラボレーション)モデルの“機能”なのだ。人間は、判断、ドメイン知識、そして説明責任を持ち込む。ツールは、スピード、幅、そして非常に大きなコンテキストを同時に保持できる能力をもたらす。互いの役割を相手が担うわけではない。
マーカスが、この仕組みを説明しようとするときに最も考えるのはコンテキストだ。
ツールにはセッション間の記憶がない。すべての会話はゼロから始まる。FieldOpsプロジェクトでの最初のセッションで、マーカスはコードを書き始める前にアーキテクチャに2時間を費やした。コードをより速く生成できなかったからではない。後続のすべてのセッションに対してコンテキストとして機能するほど、アーキテクチャを正確にドキュメント化する必要があったからだ。
アーキテクチャのドキュメントは納品物ではなかった。それはインフラだった。それは、その後のあらゆるセッションが構築された基盤であり、安定した参照情報だった。だからツールは、生成したコードをただ出すだけではなく、生成されたコードを仕様書に照らして確認するように依頼できた。質問の中に仕様が暗黙に含まれていると期待するのではなく、だ。
これが、多くの人がスキップしてしまう規律だ。彼らはドキュメント化する前に生成を始める。最初のセッションは生産的に見える。画面にコードが現れ、いろいろなことが起きる。だが生成されたコードは、生成されてきたコンテキストと同じくらいしか正しくない。コンテキストが薄ければ、出力も薄くなる。流暢に読める。コンパイルできる。何かができる。正しいことをしているかどうかは別問題で、それが現れるのは、たいてい後になって、しかも最悪のタイミングでだ。
マーカスは、初期の段階で一度だけこの間違いをして学んだ。何が起きているのかを言語化する前のことだ。彼は、最初にそれが永続化すべきアグリゲートをドキュメント化せずに、リポジトリの実装を要求した。生成されたコードは綺麗で、きちんと構造化され、適切に型付けされていた。だが、それは過去3日間で設計していたものとは一致しないドメインモデルに対する永続化レイヤーを構築していた。見つけるのに1時間かかった。見つけられなければ、何日もかかったはずだ。
学びは「ツールが間違えた」ではなかった。学びは「私が間違ったコンテキストを与えたのに、やつは私の頼んだ通りのことを正確にやってしまった」だった。
この区別こそが、すべてであると分かってきた。
多くの長いセッションには、どこかで—3時間目を過ぎたあたりで—解決が進まず、同じエラーが少しずつ違う形で現れ続け、そして疑念の声が聞こえ始める瞬間がある。
マーカスは自分に正直に言ってきた。疑念の声は非合理的ではない。実データに対するパターン認識なのだ。問題が解決しない。セッションは長くなっている。現実のチームなら複数の人が取り組んでいるはずだ。あるいは、まだ見つけていないだけで、アプローチには限界があるのかもしれない。
彼が学んだのは、疑念の声は診断については当たっていることが多いが、結論については外れているということだ。セッションが長くて生産的でないことは実データだ。そのデータからアプローチが根本的に欠陥だという結論が導かれるわけではない。よりあり得る結論は、セッションの中でノイズが溜まりすぎたのだ。試しては切り捨てたものが多すぎる。失敗の履歴がシグナルと競合しすぎている。そして必要なのは放棄ではなくリセットだ。
リセットは不快です。喪失のように感じるからです。セッション内で蓄積されたもの、組み立てられた文脈、却下したアプローチ、ようやく有用な応答を生み出した、あの特定の言い回し——それらが消えてしまうように思えます。そして実際に、その一部は消えます。ですが正しい種類のリセットは、信号を保持し、ノイズを捨て去ります。構造化された引き継ぎとして、5分間、確実に分かっていること、試したこと、未解明のままのことを書き留めるのは、文脈の喪失ではありません。文脈の圧縮なのです。新しいセッションは、蓄積した苛立ちではなく、必要不可欠な情報から始まります。
Marcusは、セッションをリセットする必要があるときの感覚を認識するほど、これを何度も繰り返してきました。これは、単に「もう少し時間が必要なセッション」と同じではありません。「もう少し時間が必要なセッション」は、欲しいよりも遅い進捗のように感じます。一方で「リセットが必要なセッション」は、同じ地面を何度も行き来しているように感じられます。
この違いは重要です。片方は粘り強さを求めます。もう片方は明確さを求めます。
Marcusが今知っているのに、14週間前には知らなかったことがあります。これは技能だということです。
トリックではありません。暗記するためのプロンプトテンプレートでもありません。従うだけのベストプラクティスのリストでもありません。技能です。意図的な練習を通じて育ち、領域への理解が深まるほど良くなり、上限は、ツールの能力ではなくあなたの判断の質によって決まるものです。
これらのツールで最も効果的になる開発者は、最も速く使い方を覚える人たちではありません。ツールができること/できないことの、より明確なモデルを育てる人たちです。生成の前に文脈へ投資する人たちです。すべての出力を、承認すべき納品物ではなく、検証を要する下書きとして扱う人たちです。そして、流暢さと正確さの違いを知るための領域知識を持っている人たちです。
DerekはMarcusより能力が低いわけではありません。いくつかの点で彼のほうがより良いエンジニアです。より経験があり、より体系的で、特定の種類のエラーを見つけるのが速い。ですが、彼がまだ更新していないのは「そのツールとは何か」というモデルです。彼はそれをコード生成器として使っていました。あれはコード生成器ではありません。コードを生成できる思考のパートナーです。その区別は、言葉の意味の問題のように聞こえるかもしれません。ですがそうではありません。それは、あなたがそれとどうやり取りするか、何を聞くか、何を検証するか、そして何を信頼するか——すべてを変えます。
デモの最後にDerekが尋ねた、「やり直すなら、何を違うふうにしていただろうか」という質問は正しいものでした。自分のモデルを更新しようとしている人の問いでした。Marcusはそれ以来、自分の答えについて考えてきました。
彼なら、より速く使おうとする時間を減らし、より良い使い方を突き止める時間を増やしたでしょう。より早い段階で文脈に投資したはずです。出力の質は、質問そのものだけでなく、全体の枠組み、制約、前提を引き出して明示することへの招待、想定外のケースを出すことへの招待、仕様が曖昧な箇所を示すことへの招待など、入力の質の関数なのだと、もっと早く学んでいたでしょう。
ツールが信頼できるかどうかを気にする時間は減らし、代わりに自分がそれに対して正しい質問をしているかどうかに、より注意を払ったはずです。
もう1つ、言う価値があることがあります。Marcusが、それを経験していない人に説明するのがいちばん難しいと感じることです。
協働は、思考の質を変える。
ツールがあなたの代わりに考えるからではありません。しません。あなたの思考は依然としてあなたの思考です。ですが、どんな瞬間でも、能力があり忍耐強く、文脈を理解している相手が常にそばにいることには何かがあるのです。作業の進行のたびに、自分の考えを口に出して、その空間で返答が返ってくること。明確化の質問をしてくれること。推論に穴があると踏みとどまらせてくれること。沈黙よりも良い思考を生み出すこと——そうしたことが、あるのです。
Marcusは以前、1人で働いていました。ソロ開発がどんな感覚かは知っています。孤立した状態で下した決定は、現実にぶつかるまでは正しいように見える。挑戦されることのなかった前提は、挑戦してくれる相手がいないからそのままになる。修正するのが「モデルを更新すること」ではなく「ミスを認めること」に感じられてしまい、アーキテクチャの選択が固まってしまう。
これは違いました。ツールが不完全でないからではありません。そうではありません。そしてMarcusは、それを証明するデバッグセッションを持っています。ですが、協働の枠組みにより、あらゆる判断が対話の中で行われ、すべての前提が表に出され検討され、モデルを更新することが、恥ずかしい修正ではなく自然な前進として感じられたからです。
AI駆動の開発におけるボトルネックは、Marcusは記録ログにどこか8週目あたりでこう書いています。「AIの能力ではない。常に、人間の判断の質、人間の目標の明確さ、そして人間が提供する文脈の豊かさだ。」
書き終えたあと、彼は少し止まって読み返しました。
「これは実は良い知らせだ。」と、彼は付け加えました。
彼は今もそう思っています。



