🐧

「AIが全部やってくれる」は幻想だった【Claude Code開発の失敗録 #1】

に公開

これはシリーズ「Claude Codeだけで開発したら技術的負債まみれになった話」の第1回です。

外から見たところ

まず、今動いているものを見てほしい。

Linux学習Webアプリ「Penguin Gym Linux」というものを作った。ブラウザ上でLinuxコマンドを練習できるサービスである。

数字だけで言えば、HTMLが100ファイル、JavaScriptが95ファイル。学習記事は40本近くあって、日本語と英語の両方に対応している。クイズは90問以上。ご丁寧に自動テストも完備。現在もNetlifyで元気に本番運用中である。

それなりの規模、と言っていいのではないかと思う。

ちなみにこのサイトは完全な静的サイトだ。データベースはない。記事の一つ一つが独立したHTMLファイルとして存在している。今どきのWebサイトは大抵、記事をデータベースに入れてテンプレートで動的に表示するものだが、このサイトにそんな洒落たものはない。HTMLファイルが物理的に並んでいる。令和の時代にホームページビルダーかと言われそうだが、完全静的サイトで運用すると決めたのだ。理由はjQueryおじさんだからというのもあるが、Netlifyだけで完結させたかったというのもある。Next.jsやAstroを使えば、Markdownファイルからページを自動生成できるのかもしれないが、知らない。知らないものは使えない。この言い訳は今後も頻繁に出てくる。

そしてこのコード、画像を除いて、すべてClaude Codeが書いた。

人間はほぼ一行もコードを書いていない。人間がやったのは「いい感じにして」と言うことだけである。

「すごいじゃん」と思った方もいるかもしれない。
私もそう思っていた。半年前までは。

中を開けたところ

では中身のほうも見ていただきたい。

アプリの基底クラス base-app.js は1,187行ある。

1,187行だ。

何でもかんでも詰め込んだ結果こうなったのだが、このファイルを開くとエディタが少し考え込む。人間も考え込む。ここに何が書いてあるのか、正直よくわからない。書いた本人であるClaude Codeにもたぶんわかっていないのではないかと思う。

状態管理のためにManagerクラスというものが4つあって、合計2,930行ある。Reactというフレームワークを使えば数十行で済むらしい内容が、2,930行かけて手作業で再発明されている。車輪の再発明という言葉があるが、これは車輪どころか車軸から再発明している。

CSSには legacy/_style.scss という778行のファイルがある。名前のとおりレガシーである。AIが生成したCSSの残骸を、SCSSという別の形式で包んだだけのファイルだ。中身は触りたくないので包んだ。臭いものに蓋をしたのと同じ要領である。これを解体するのに7回のフェーズを要した。7回だ。引っ越しでも7回はやらない。

これが「Claude Codeだけで作った本格Webアプリ」の正体である。

外側はきれいで、中は廃墟。

そういうマンションをテレビの特集で見たことがある。まさか自分が建てる側になるとは思わなかった。人生というのは何が起こるかわからないものだ。

そもそもの話

そもそもの始まりは、職場のジュニアエンジニアだった。

彼らはLinuxコマンドをあまり知らない。

本当は教えたいのだが、踏み出せない。「ちょっとLinuxの話していい?」と声をかけるだけで、説教くさい昭和のおじさんだと思われるのではないか。そういう懸念が先に来てしまう。そもそも今の開発環境はものすごく便利なものに溢れていて、黒い画面に白い文字を打たなくても仕事はできてしまうのだ。わざわざ教える大義名分がない。

でも知っていたほうがいいと思う。たぶん。

じゃあ楽しく触れる環境を作ればいいのでは。ブラウザで動く仮想ターミナルなら、インストール不要で誰でもすぐ使える。ゲーミフィケーションを入れれば、少しは学べるかもしれない。

どうせなら世の中の役に立つものがいい。あわよくばタバコを1日一箱くらい買えるお小遣いがもらえたらいいな、と思った。月にすると1万5千円くらいだろうか。ささやかすぎる夢だが、夢は小さいほうが傷も浅い。

ここで自己紹介をしておく。

私はもともとサーバサイドのエンジニアだった。LinuxやPHPでバックエンドを作っていた時代もある。フロントエンドはjQueryでどうにかしていた。いわゆるjQueryおじさんである。

ただ、歳をとるにつれてコードを書く機会は減った。気がつけば、若手に仕事を振って進捗を気にするだけの役回りに回されていた。マネジメントというらしい。偉くなったわけではなく、他にやる人がいないから順番が回ってきただけだ。

世の中にはとんでもなくコードが書ける人がたくさんいることも、だんだん見えてきた。そういう人たちを遠くから眺めているうちに、自分が現役に戻って戦う絵がどうしても浮かばなくなった。コードで戦うのは、もうだいぶ前に諦めたのだと思う。悔しいというよりは、ああそうですか、という感じである。熱が冷めたというより、そもそも熱があったのかも、今となっては怪しい。

ReactもNext.jsもTailwind CSSも名前は知っているが、自分で書いたことはない。書いたことがないものは怖い。怖いものには近づかない。中年とはそういう生き物である。

そんな私がClaude Codeの存在を知った。AIにコードを全部書かせるバイブコーディングというやつだ。これを使えば、コーディングの腕に自信がなくても本格的なアプリが作れるという。

つまり私はClaude Code初心者であり、バイブコーディング初心者であり、そもそも最近のフロントエンド全般の初心者だった。初心者の三冠王である。冠が多いほど偉いわけではない。

要件定義はしなかった。何ページ必要で、どんな機能があって、データ構造はどうで、みたいなことは一切考えなかった。「Linuxコマンドを学べるWebアプリ」。以上である。飲食店を開くのに「おいしい店」としか決めていないようなものだ。メニューは? 席数は? 予算は? 何も決めていない。

バイブコーディングという言葉がある。AIと雰囲気で開発するスタイルだ。私はこれを文字通りに受け取っていた。雰囲気でいいのだ、と。ノリと勢いでAIに話しかければ、AIがいい感じにしてくれる。要件定義なんて堅いことは、バイブコーディングには似合わない。そう思っていた。

現実はそうではなかった。バイブコーディングの「バイブ」は、好き勝手にやるという意味ではない。少なくとも、好き勝手にやった結果は好き勝手なコードになるだけだった。それを後から知ることになる。

技術選定はすぐ決まった。Vanilla JSとVanilla CSSである。ReactやVueなんか使わせたら、AIが書いたコードが読めない。せめて自分が知っている技術なら中身を確認できるだろう。そう考えた。

後から知ったことだが、AIとフレームワークの相性はとても良いらしい。Reactには「コンポーネント」という決まった型がある。TypeScriptには「型」という制約がある。Tailwind CSSには「ユーティリティクラス」という設計ルールが組み込まれている。

AIは制約があるほど安定した出力を出す。自由にさせると、毎回違うものが出てくる。

私は最も自由な環境を選んでしまったのだ。この判断がどれほど間違っていたかは、第2回で切々と語る。

天国だった頃

最初の数日は天国だった。

「Linuxコマンドを学べるWebアプリを作って」

Claude Codeにそう伝えると、ターミナルのUIが出てきた。コマンドを打つと結果が返ってくる。仮想ファイルシステムまで実装されている。

すごい。すごいぞこれは。

「課題を追加して」「バッジ機能を追加して」「ダッシュボードを追加して」

言えば言うほど機能が増える。3日でプロトタイプが動いた。

私は天才かもしれないと本気で思った。コードを一行も書いていないのに天才を名乗るのはいかがなものかという気もするが、そのときは気にならなかった。気分がいいときの人間は、だいたいのことが気にならないものである。

毎日が楽しかった。朝起きて、Claude Codeに話しかけて、夜には新しい機能ができている。プログラミングとはこんなに楽しいものだったのか。

いや、プログラミングはしていない。「いい感じにして」と言っているだけだ。

でも楽しかった。楽しかったのだ。過去形なのが気にかかると思うが、過去形なのである。

小さな違和感たち

異変は、気づかないうちに始まっていた。

今思えば予兆はあった。淡々と並べてみる。

  • ページごとに色が微妙に違う。同じサイトのはずなのに、ページを移動すると少し色が変わる。模様替えをした覚えはない
  • ボタンに謎のグラデーションがかかっている。なんだか虹色のボタンもある。押すと何かいいことがありそうな雰囲気だが、特にない
  • 見出しにことごとく絵文字がついている。「🚀 学習を始めよう!✨」「📚 コマンド一覧」「🎉 おめでとう!」。どこを見ても絵文字が目に入る。私は絵文字をつけてくれとは一度も言っていない。AIが勝手につけるのだ。たぶんAIは、絵文字をつけると人間が喜ぶと思っている。喜ばない。
  • commands.js というファイルがどんどん太っていく。最初は100行だったのが、気づくと巨大な何かになっている。見て見ぬふりをする
  • 「あとで直そう」が口癖になっている。あとでというのは便利な言葉だ。だいたいの場合、永遠に来ない
  • そもそも何を作るのか決めていないので、思いつきで機能が増えていく。AIは頼めば何でも作ってくれるので、歯止めがない
  • PC版とモバイル版を別々のHTMLで作ってしまった。同じアプリなのに入口が2つある。スマートフォンが世に出始めた頃、多くのWebサイトが「PC版はこちら」「スマホ版はこちら」とやっていた。あの時代を令和に再現してどうするのだ。歴史から学ばない者は同じ過ちを繰り返すとは言うが、まさか自分のことだとは思わなかった

一つ一つは小さなことだった。

でも小さな違和感というのは、だいたい小さくないのだ。虫歯みたいなものである。痛くないうちは放っておける。放っておくと神経までいく。

私はそのとき、すでに虫歯だらけだったのだと思う。ただ痛みに気づいていなかっただけだ。痛くなったときにはもう手遅れ、というのは虫歯も技術的負債も同じである。

この先の話

このシリーズでは、ここから先に起きたことを語る。

第2回「技術選定を舐めた代償」 では、Vanilla JSを選んだ話をする。結論だけ先に言うと、おまえはClaudeが書いたコードを読まない。

第3回「デザインシステムを知らなかった」 では、AIにCSSを丸投げした結果、サイト全体がグラデーションと絵文字で埋め尽くされた話をする。のちに設計哲学書へ「グラデーションは逃げ」と書くことになる。

第4回「CLAUDE.mdはAIの脳になった」 では、失敗から学んでAIへのルール整備を始めた話をする。自動ワークフローもAIに作らせた。あとから似たOSSがたくさんあることを知った。

第5回「i18nとSEO」 では、「あとで対応」がどれだけ高くつくかを書く。翻訳キー2,000個をClaude Codeに後から整備させたときの、「直った」「いや直ってない」「そこじゃない」「だからそこじゃない」を繰り返す不毛なやり取りは、もう二度と味わいたくない。

第6回「AdSenseとNetlify」 では、収益化とデプロイの地雷を踏んだ話をする。本番で英語版が全滅していたことに、だいぶ経ってから気づいた。

第7回「セッションとコンテキスト」 では、AIの記憶喪失と戦った記録を。

そして最後の 第8回「半年間の全記録」 で、教訓と生存戦略をまとめる予定だ。

全8回を読み終えたとき、あなたは私と同じ失敗をしなくなる。たぶん。

タバコの話

半年が経った。正確に言うと、半年のうち実際に手を動かしていたのはその半分くらいだと思う。忙しくて触れない時期もあったし、心が折れてしばらく放置した時期もあった。

ブラウザで動くLinuxコマンド練習サイトは動いている。ありがたいことに、少しずつユーザーも来てくれるようになった。記事も40本近くある。自動テストも走っている。

タバコ代には程遠い。

技術的負債の返済方法だけは少し詳しくなった、かもしれない。

返済が終わるめどは立っていない。でもアプリは動いている。動いているものには価値がある。たぶん。

誤解しないでほしいが、Claude Codeがダメだという話をしたいのではない。Claude Codeがなければ、このアプリは影も形もなかった。失敗したのは道具ではなく、道具の使い方を知らなかった私のほうだ。

同じ経験がある方は、たぶん今ごろ遠い目をしていると思う。

Claude Code初心者の方へ: 「天国だった頃」や「小さな違和感たち」に心当たりがある方、教えてほしい。虫歯の段階で気づけた人もいれば、神経まで達した人もいるはずだ。仲間がいるとわかるだけで救われる。

Claude Code上級者の方へ: 半年前のこの状態の私に、あなたならどんな一手を打ちますか。私の仮説は「作り始める前に30分だけReactのチュートリアルを触ること」です。たった30分を惜しんだことが、後続の全てを決めた気がしている。これが私の叩き台です。「そこじゃない」でも「その一手では足りない」でも、遠慮なく殴ってください。過去の私はもう手遅れですが、現在の私が受け取ります。

次回、Vanilla JSという選択がいかに間違っていたかを語る。


次回: 技術選定を舐めた代償 — Vanilla JSという茨の道【Claude Code開発の失敗録 #2】

Discussion