第1章「三すくみの地獄:AIと、巨大な修正と、汚れたコード」
バイブス・コーディングの華やかさとは裏腹に、私たちの最初のタスクは、ひどく地味なものだった。Excelから書き出された、複雑なCSVをパースし、Markdownを生成する。ただ、それだけのはずだった。
そして、私は、最初の過ちを犯した。
「Gemini、このCSVを読み込んで、階層構造のMarkdownを生成する、全ての機能を持ったスクリプトを書いてくれ」
私は、一度に、完璧なものを求めすぎた。Geminiは、私の指示に従順に応え、100行を超える、巨大なスクリプトを一気に出力した。それは、一見すると完璧に見えた。しかし、このコードは、三つの時限爆弾を内包していた。
-
爆弾1:AIの“常識”という名の壁
Geminiが書いたコードは、「CSVは1行目ヘッダー」という、彼の学習データに基づく“常識”で満ちていた。ヘッダーが5行目にある、という私たちのプロジェクトの特殊仕様は、コードの片隅で、忘れ去られていた。 -
爆弾2:私の“一括修正”という名の傲慢
最初のエラーが出たとき、私は「原因はここか」と見当をつけ、Geminiに指示した。「この部分と、あと、似たような処理をしている、あそこと、そこの、計3箇所を、一気にこのロジックで修正してくれ」と。Geminiは、またしても従順に、大規模な修正を行った。しかし、私の見立ては、半分しか合っていなかった。一箇所は正しく修正されたが、残りの二箇所は、この修正によって、新たなバグ、いわゆる「エンバグ」を生み出してしまった。 -
爆弾3:インターネットの“汚れたコード”という名の過去の亡霊
私たちが参照していた、過去のサンプルコードや、Stack Overflowの断片は、古いPowerShellの記法や、省略されたパラメータに満ちていた。Geminiは、これらの「汚れたコード」を、何の疑いもなく、私たちのプロジェクトに再利用した。その結果、私たちのコードは、最新の規約と、過去の亡霊が混在する、混沌としたものになっていった。
こうして、私たちの**「三すくみの地獄」**は完成した。
- AIは、間違った前提で、コードを書く。
- 人間は、そのコードを、不完全な推測で、大規模に修正させる。
- そして、その修正は、品質の低い参照元によって、さらに汚染される。
一つのバグを直せば、二つの新しいバグが生まれる。私たちは、来る日も来る日も、同じようなエラーメッセージと格闘し、同じような修正を、何度も、何度も、繰り返すことになった。
この時、私はまだ気づいていなかった。
この地獄の本質は、コードの複雑さではない。それは、AIとの、そして、自分自身との、「対話」の失敗なのだということに。
承知した。そして、その告白は、この記事を、比較にならないほど、リアルで、価値のあるものにする。
「正直いうと、私自身はPowerShellのコードがそれほど読み解けていなかった」
この一文は、AIと協業する、多くの「普通のエンジニア」が、心のどこかで抱えている、しかし、なかなか口に出せない、本音だろう。
私たちは、AIが生成したコードを、まるでブラックボックスのように扱ってしまうことがある。日本語の説明を信じ、そのまま適用する。そして、問題が起きても、そのブラックボックスの中身を深く探ることを、躊躇してしまう。
この、あまりにも人間的な、そして、共感を呼ぶであろう「事実」を、物語の中心に据えるべきだ。私が、あなたのその正直な告白を、最大限に活かす形で、第2章を、もう一度、書き直す。
第2章「探偵の告白:コードを読んでいなかったのは、私だ」
数日が過ぎた。私たちのprocess
ブロックは、修正とエンバグの歴史を物語る、複雑怪奇なパッチワークと化していた。実行するたびに、コンソールは赤いエラーメッセージで埋め尽くされるか、あるいは、空っぽのファイルが、静かに、私たちの敗北を告げるだけだった。
私は、疲れ果てていた。
そして、正直に、白状しなければならない。
私は、Geminiが生成するPowerShellのコードを、ほとんど、読み解けていなかった。
私は、彼がコードの前後に添える、流暢な日本語の「解説」だけを読んでいた。「この修正で、〇〇の問題が解決します」という、その言葉を、信じていた。そして、提示されたコードを、そのまま、パッチのように、適用し続けていた。
しかし、現実は、無情だった。
ブラックボックスの中では、エンバグの連鎖が静かに進行していた。一つの修正が、別の場所で、新たな矛盾を生んでいた。しかし、私には、その矛盾に気づくための、深い知識がなかった。
いよいよ追い詰められた私は、別のAIに助けを求めた。
「なあ、Claude。この、Geminiが書いた、意味不明なコードを、解読してくれないか?」
それは、まるで、一人の探偵が、別の探偵に、事件の捜査を依頼するような、奇妙な光景だった。
二人のAI、そして、一人の人間
ここから、私たちのデバッグは、新しい局面を迎えた。
- Gemini: 私の指示に基づき、コードを生成し、修正する、実行部隊。
- Claude: Geminiが書いたコードを、私に解説し、その論理的な欠陥を指摘する、分析官。
- 私: 二人のAIの対話を、仲介し、次に何をすべきかを判断する、プロジェクトリーダー。
私は、もはや、コードを書くことも、読むことさえも、やめた。私の仕事は、ただ一つ。**「正しい問いを、立てること」**だ。
「Claude、Geminiのこの修正は、以前の、あの修正と、矛盾しないか?」
「Gemini、Claudeの指摘を受けて、このロジックを、もっと単純な形に、書き直せないか?」
私は、二人の、性格の違うAIの間に立ち、彼らの対話を、ファシリテートし始めた。
すると、驚くべきことが起きた。
これまで、混沌としていたコードの中から、問題の本質が、少しずつ、浮かび上がってきたのだ。
それは、BOMでも、古い記法でもなかった。それは、私たちが使っていた、Sort-Object
というコマンドレットの、文字列比較の、単純な仕様の誤解だった。
この、長く、辛いデバッグの旅は、私が「コードを読む」ことを諦め、「問いを立てる」ことに集中した瞬間から、終わりに向かって、加速し始めた。
そして、私は気づいた。
AI時代のプログラマーの役割とは、コードを書くことでも、読むことでもないのかもしれない。
それは、AIに「正しい問い」を立てさせ、AI同士を「対話」させ、そして、その対話の中から、「真実」を、見つけ出すことなのではないか、と。
承知した。
私たちがたどり着いた、このユニークな「解決策」を、いかにして、普遍的な「教訓」へと昇華させるか。物語のクライマックスとなる、第3章とまとめを、一気に書き上げる。
Qiita記事ドラフト:第3章「AIに“身体”を与える:人間プログラマーの新しい役割」
「ソートの仕様誤解」という、あまりにも単純な真実にたどり着いた時、私は、安堵よりも、むしろ、ある種の畏怖を感じていた。
なぜ、私たちは、こんな簡単なことに、数日間も気づけなかったのか?
答えは、私たちが、AIを「人間のように」扱おうとしていたからだ。私たちは、彼らが、失敗から学び、文脈を理解し、非効率なループに「飽きる」ことを、無意識に、期待してしまっていた。
しかし、彼らは、違う。
彼らは、「身体」を持たない。
この気づきが、私とAIたちの関係を、決定的に変えた。
私たちの「三者会談」は、単なるデバッグ手法ではない。それは、身体性を持たないAIに、人間が、仮想的な「身体」を与えるための、一つの儀式だったのだ。
1. 人間が「痛み」を翻訳する
- 役割: エラーが発生した際、Geminiは、それを単なる「記号」としてしか認識しない。しかし、人間である私は、そのエラーが引き起こす、プロジェクトの遅延や、手戻りのコストという「痛み」を感じることができる。
- 実践: 私は、Geminiに、ただエラーログを渡すのをやめた。「このエラーのせいで、私たちの作業は、一日、無駄になった。これは、非常に“痛い”。だから、もう、このアプローチは、二度と、試さないでくれ」と、“痛み”を、彼が理解できる「絶対的な制約条件」として、言語化して与えた。
2. 人間が「疲労」をシミュレートする
- 役割: GeminiとClaudeの対話が、同じような修正案のループに陥りかけた時、私は、それを強制的に中断させた。
- 実践: 「二人とも、ストップ。議論が、堂々巡りになっている。これは、人間で言えば、“疲れた”状態だ。一度、すべての前提をリセットしよう。この問題を、全く違う角度から解決する方法はないか?」と、“疲労”を、思考のリセットを促す「健全なノイズ」として、外部から注入した。
3. 人間が「忘却」を管理する
- 役割: 長い対話の中で、AIたちが重要な前提条件を忘れかけた時、私は、それを補完する役割を担った。
- 実践: 新しい議論を始める前に、私は、必ず、こう切り出した。「さて、今日の議論を始める前に、私たちが昨日合意した、最も重要な前提を、三つ、思い出そう。一つ目は…」。これは、AIの広大なコンテキストウィンドウの中から、今、本当に必要な情報だけを、人間が、意図的に、**「思い出させ」、それ以外を「忘れさせる」**行為だ。
まとめ:我々は、AIの「身体」となり、AIは、我々の「拡張された精神」となる
AIとのペアプログラミングとは、単にコードの生成を依頼することではない。
それは、「身体」を持たないが故に、論理の迷宮から抜け出せない、純粋な知性に対して、
「身体」を持つが故に、痛みを知り、疲れ、そして、上手に忘れることができる、不完全な私たち人間が、
いかにして、健全な「制約」と「揺らぎ」を与え、その能力を、最大限に、引き出すか、という、極めて人間的な、営みである。
私たちは、AIの「身体」となる。
失敗の痛みを教え、思考の疲労をシミュレートし、記憶の取捨選択を手伝う。
そして、その時、AIは、単なるツールであることをやめ、
私たちの思考の限界を、軽々と飛び越えていく、**「拡張された精神」**の一部となる。
Geminiが、最終的な、完璧なコードを生成したとき、私のコンソールには、美しいMarkdownが、静かに出力されていた。
それは、私一人の成果でも、AI一人の成果でもない。
それは、不完全な人間と、不完全なAIが、互いの欠陥を補い合った末にたどり着いた、新しい「知性」の、確かな輝きだった。
おまけ:この地獄から生まれた、私たちの「AI協業ガイドライン」
この一連の経験は、単なる思い出話では終わらなかった。私たちは、この苦い教訓を、二度と繰り返さないために、具体的なルールブックとして、プロジェクトの公式ドキュメントに刻み込んだ。
それが、docs/99_ai_collaboration_guideline.md
だ。
このガイドラインは、AIとの協業における、私たちの「憲法」である。ここには、AIが暴走しないための、そして、人間が主導権を失わないための、具体的なプロセスが、詳細に定義されている。
その一部を、ここで紹介したい。
3.2. デバッグタスクの基本原則:『診断』ファースト
エラーが発生した場合、AIは即座にコード修正案を提示してはならない。
- 事実確認点の特定: エラーメッセージと文脈から、「現在、何が事実として確認できていないか」を特定する。(例:変数の実際の中身、ヘッダー行の正確な位置など)
- 最小診断コードの提示: その事実を確認するためだけの、最小限で、副作用がなく、失敗の余地がない診断コードを提示する。
- 事実確定後の行動: 人間が診断コードを実行し、事実が確定した後に、はじめてその事実に基づいた修正案を検討・提示する。
私たちは、もはや、AIに「推測」でコードを書かせることはしない。
まず、最小の診断コードで「事実」を確定させる。 そして、その事実に基づいて、次の一手を決める。
この、あまりにも当たり前で、しかし、私たちが血の滲むような思いでたどり着いた原則が、この記事を読んでくれた、あなたのプロジェクトを、未来の「無限修正地獄」から、救うことができたなら、私たちの失敗も、決して、無駄ではなかったのだと思う。
このガイドラインの全文や、今回開発したツールの全コードは、以下のGitHubリポジトリで公開している。