シニアエンジニアが金曜の夕方に、AIが書いた3,000行を消した話

2026-09-20
I don't want to discover what my code does during the incident.
機能は動いていた。テストも通っていた。社内デモの評判も良かった。それでも、本番でこれを背負う当人はこう言った。「自分が理解していないコードで叩き起こされる人間には、なりたくないんです」
Ethan がブランチを消したのは、金曜の午後だった。
3,084行。11日分の仕事。社内にはもう見せてある機能。テストはグリーン。プロダクトも満足していた。
金曜の16時42分に消していいものではない。Slackを事件現場にする趣味でも ない限りは。
通知を見て、すぐメッセージを送った。「何があった?」
返事は「何も」。
まったく安心できない。電話をかけた。障害が出たわけではない。セキュリティの問題もない。データが壊れたわけでも、裏に設計上の破綻が潜んでいたわけでもない。コードは動いていた。
だから混乱した。「じゃあ、なんで消したんだ」
Ethan は少し黙ってから、こう言った。
「これが深夜2時に壊れたとき、自分で直せるかどうか分からないので」
その一言で、会話の性質が変わった。
Ethan は、そんなことを言いそうな最後の一人だった
彼はバックエンド一筋で9年近くやってきた。
地獄のようなマイグレーションを担いだこともあるし、会社の半分が見守る中で本番障害をデバッグしたこともある。
バックエンドエンジニアが一度は通る「誰かがKafkaを発見して、あらゆる関数がイベントになるべきだと言い出す時期」も生き延びている。
複雑なコードを怖がる人間ではない。そしてAI否定派でも、まったくない。
むしろ逆だ。チームで一番AIを使い倒していたのは Ethan だった。自分なりのワークフローまで組み立てていた。モデルと一緒に設計を詰める。退屈な部分を生成させる。レビューする。テストを回す。エッジケースを洗い出させる。リファクタする。この繰り返し。
速かった。かなり速かった。
彼が消したあの機能は、従来のやり方なら3〜4週間はかかったはずだ。それを11日で作り上げた。
本来なら、これは成功事例だった。
なのに彼は消した。
「理解できていない箇所を見せてくれ」
そう頼んだ 。
ひどい生成コードの関数を指差すのだろうと思っていた。ところが彼はリポジトリを開いて、ただスクロールし始めた。
「それが問題なんです」
「どういうことだ」
「一箇所じゃないんですよ」
サービスをクリックする。次にマッパー。リトライの実装。バリデーション。外部APIのラッパー。
彼は個々の部品をすべて理解していた。だからこそ、次の言葉が興味深かった。
「見ている間は全部分かります。でも、システムが頭の中に入っていない」
言いたいことは、よく分かった。
あるシステムに長く関わっていると、ある時点からコードを「読む」のをやめる。
形が分かるようになる。ここを変えたら、あっちの何かが動く気がする、という感覚が先に来る。
このトランザクションに触れた瞬間、反対側のリスナーを思い出す。
誰かが「この変なリトライ、いらなくないですか」と言うと、理由を思い出す前に脳が「絶対ダメだ」と言う。
魔法ではない。メンタルモデルだ。
Ethan は3,000行を作りながら、それを作っていなかった。
コードが速く届きすぎた
それが彼の見立てだった。
普通、機能を作るということは、問題の内側に時間を費やすことを強制される。何かを書く。動かない。前提が間違っていたと気づく。設計を変える。DBのモデルが扱いづらいことに気づく。誰だこんな設計をしたのは、と悪態をつく。
そして git blame が、それを設計したのは18ヶ月前の自分だと教えてくれる。
謙虚になる。でも、役には立つ。
機能が完成する頃には、コードを書き上げただけではない状態になっている。
コードの側が、自分の中に何かを作っている。
地図だ。
AIを使ったことで、Ethan はその大半を飛ばしていた。欲しいものを説明する。エージェントが実装を出す。レビューする。手を入れる。質問する。テストを走らせる。次を生成させる。またレビューする。
一つひとつの手順は、どれも妥当だった。
ただ、実装が育つ速度が、彼の中のモデルが育つ速度を上回っていた。
その差分を、彼は感じ取っていた。
リポジトリのほうが、その機能の責任者より、機能について詳しくなっていた。
少し意地の悪い質問をしてみた
「モニターを消すから、この機能を説明してくれ」
彼は笑って、やってみた。
最初の1分は問題なかった。リクエストがここに来る。バリデーション。処理。外部プロバイダ。永続化。
そこから質問を始めた。
「プロバイダは成功したのに、永続化が失敗したらどうなる?」
間。
「補償処理があります」
「どこに?」
もっと長い間。彼はノートPCを開こうとした。止めた。
「リトライは何にかかってる?」
考える。
「プロバイダ呼び出しは、確実に」
「DBの操作は?」
また間。
「同じイベントが2回来たら?」
冪等性のロジックがあることは分かっていた。どこにあるかは、思い出せなかった。
機能は壊れていなかった。
壊れていたのは、Ethan のオーナーシップのほうだった。
この2つは、まったく別物だ。
そして彼は、忘れられないことを言った
背もたれに体を預けて、彼は言った。
「自分のコードを 、他人のコードみたいにレビューしてたんだと思います」
それだ。
11日間、作っていたのはAIだった。
Ethan はレビュアーになっていた。
そしてレビューという行為は、システムとの間に、作ることとはまったく違う関係を作る。
手で作ると、不格好な判断のひとつひとつが小さな傷跡を残す。
あの奇妙な条件分岐がなぜあるのか覚えているのは、素直な実装がなぜ失敗するのかを2時間かけて突き止めた後に、それを書いたからだ。
生成されたコードをレビューする場合、同じ条件分岐を完璧に理解できる。
だが、見て理解できることと、本番が落ちている最中になぜそれが存在するのかを再構築できることは、別の能力だ。
再認は、想起ではない。
そして本番環境は、想起のほうを試してくる。たいてい、こちらが疲れているときに。
我々は、間違ったものを測っていた
AI導入の数字は素晴らしく見えていた。サイクルタイム短縮。実装時間短縮。PRスループット向上。
データはあった。経営陣は数字が好きだ。特に右肩上がりの数字が。
ただ、こういう指標を持っている人はいなかった。
このシステムの責任者は、ハッピーパスがハッピーでなくなったときに何が起きるかを説明できるか?
Jiraには収まらない。だから測らなかった。
我々は、AIによって測りやすくなったもの——アウトプット——を測っていた。
そして Ethan のアウトプットは、見事だった。
しかし金曜の16時42分、その機能で最も高いアウトプットを出したエンジニアが、深夜2時にそれを背負う自信がないと言った。
アウトプットへの関心が、急速に薄れた。
一番怖いのは、ダメなAIコードではない
ダメなAIコードは、むしろ好都合だ。目に見える。テストが落ちる。コンパイラが怒る。レビュアーが捕まえる。みんなで指を差して笑う。消す。
厄介なのは、良いAIコードのほうだ。
きれいなコード。妥当な抽象化。通るテスト。正しい実装。明らかにおかしいところがないので、レビューを通過してしまうコード。
そういうコードが、静かに別種の障害を生む。
誰も完全には所有していない。モデルが生成した。人間が承認した。別の人間がレビューした。CIが祝福した。本番が受け入れた。
全員が触った。誰も背負っていない。
そして半年後、妙なことが起きる。そのとき問われるのは、これではない。
「誰が書いた?」
これはGitが答えてくれる。
問われるのは、こっちだ。
「なぜこうなっているのかを理解している人間は誰だ?」
こちらは、ずっと難しい。
Ethan は、11日を捨てたわけではなかった
ここは、自分が最初に誤解していた部分だ。
彼は月曜の朝に白紙から始めて、主義のために3,000行を手打ちしたわけではない。それはただの馬鹿げた話だ。
彼はまたAIを使った。ただし、使い方を変えた。
機能を、いくつかの塊に分けて作り直した。
主要なセクションを生成させる前に、それが何をしなければならないかを自分で書き出した。失敗モード。状態遷移。リトライ。責任の範囲。ステップ3が成功してステップ4が失敗したら何が起きるか。
そのうえで、エージェント に実装を頼んだ。そのまま採用することもあった。書き換えることもあった。AIのほうが良い設計を出してくることもあった。
違いは、ごく微妙なものだ。
1回目、Ethan は自分がレビューする機能をAIに作らせていた。
2回目、Ethan は自分で機能を作り、実装を速めるためにAIを使った。
同じツール。まったく違う関係。
2回目のバージョンは、小さくなった
これには笑ってしまった。
3,084行が、およそ2,300行になった。
いくつかの抽象化が消えた。汎用フレームワークがまるごと一つ消えた。リトライ層は、恥ずかしいほど単純になった。
Ethan は、別のヘルパーを呼びやすくするためだけに存在していたらしきヘルパーを削除した。AIはどうやら、エンタープライズJavaを実によく学習しているらしい。
それより重要なのは、Ethan がリポジトリを開かずにシステム全体を説明できたことだ。
同じ質問で試してみた。
「プロバイダが成功して、永続化が失敗したら?」
即答。
「何がリトライされる?」
即答。
「重複イベントは?」
即答。
「この設計で、一番怖いのはどこだ?」
その答えが、一番速かった。
「補償処理のパスです。プロバイダ側の挙動が変わったら、あれは危険になります」
その瞬間に、この機能は出せると分かった。
テストがグリーンになった時ではない。
責任者が、どこから血が出ると予想しているかを語れるようになった時だ。
シニアエンジニアの給料は、diffに現れない部分に払われている
AIが書いたコードを見れば 見るほど、これがはっきりしてくる。
コードを打つコストは安くなっている。おそらく劇的に。
だが、エンジニアリングのコストが同じ比率で安くなるわけではない。
高価だったのは、そもそもタイピングではなかったからだ。
高価なのは、何が存在すべきかを知っていること。何が存在すべきでないかを知っていること。依存先が嘘をついたときに何が起きるかを知っていること。自分がどのトレードオフを受け入れているかを知っていること。前提が外れたときに何をするかを知っていること。
そして最終的には、自分が出したものについて、枕元に携帯を置いて、それに起こされる人間になってもいいと思えるだけ理解していること。
それがオーナーシップだ。
AIは実装を生成できる。ポケベルは受け取れない。
月曜の朝、消して良かったかと聞いてみた
彼は、何を当たり前のことを、という顔をした。
「はい」
「最初のやつ、動いてたのに?」
「動いてたからこそです」
どういう意味かと聞いた。
彼は言った。ダメなコードなら、直すことを強制される。危険なのは、出せてしまうほうのバージョンだ。全部の検査を通ってしまうやつ。何ヶ月も後に妙なことが起きるまで、誰も疑問を持たないやつ。
そして、こう続けた。
「自分のコードが何をするのかを、障害対応の最中に知りたくないんです」
エンジニアの口から出た瞬間に、その人の本当のシニアリティが分かる台詞がある。あれは、その一つだった。
それでも、私はAIにコードを書かせている
かなりの量を。Ethan もそうだ。
エンジニアは古の技を守る修行僧のようにボイラープレートを手打ちすべきだ、などとは微塵も思っていない。AIは有用すぎる。生産性の向上は本物だ。
ただ、「生成されたコードをレビューしたか」だけを聞くのはやめた。その基準は低すぎる。
知りたいのは、その背後にある推論を、誰かが所有しているかだ。
説明できるか。
トレードオフを擁護できるか。
最初に壊れるのはどこか言えるか。
どの前提が怖いか言えるか。
それを作るのを手伝った機械に「何が起きたと思う?」と聞かずに、デバッグできるか。
できないなら、その機能が完成しているとは思えない。
テストが全部グリーンでも。むしろ、全部グリーンなら、なおさら。
ブランチは16時43分に消えた
3,084行。削除。
当時は、Ethan が11日を捨てたのだと思った。今は違う見方をしている。
彼が捨てたもののうち、コードは一番安いものだった。
彼が出荷を拒んだのは、複雑さが自分の理解より速く育ってしまったシステムだ。
この区別は、AIが良くなるほど重要になる。
なぜなら、3,000行を生成するのに11日かかる時代は、じきに終わるからだ。11分になる。1万行が午後いっぱいで済むかもしれない。サービスまるごとが昼前に現れるようになる。
制約条件は、もはやソフトウェアをどれだけ速く作れるかではなくなる。
人間の組織が、どれだけの量のソフトウェアを本当に理解し、運用し、責任を持てるかになる。
そうなったとき、ときどきこう言うエンジニアがいる。
「動く のは分かってます。それでも、まだ出しません」
ダッシュボード上では、その人は遅く見えるかもしれない。
手放さないほうがいい。