Nexta Tech Blog
💨

AIと実践するVibe-Driven Development:第1回「"イイ感じ"で進捗表示して!」

に公開

こんにちは!株式会社ネクスタのエンジニア、bikです。

僕はこれまで、サーバーサイドの処理は同期的に書くことがほとんどでした。しかし、最近のWeb開発では非同期処理が当たり前。正直、ちゃんと理解できているか自信がありませんでした。

「このままではマズい。何か実践的なテーマで、非同期処理を深く学んでみよう!」

そう決意した僕が選んだお題は、「時間のかかる処理の進捗表示」。これなら、バックグラウンド処理、クライアントとの通信など、非同期処理の勘所がたくさん詰まっていそうです。

そして、今回の学習パートナーとして、GoogleのAI**「Gemini 2.5 Pro」**を相棒に選びました。やりたいことの雰囲気(Vibe)を伝え、AIと対話しながら実装を進める「バイブコーディング」で、僕がどこまで成長できるのか。

これは、そんな僕のリアルな学習と格闘の旅路の記録です。

すべての始まりは、一つの"学習テーマ"だった

まず僕がGeminiにぶつけたのは、こんなVibeでした。

僕: 「非同期処理の学習として、時間のかかる処理の進捗をプログレスバーで"イイ感じ"に表示する機能を作ってみたいんだけど、どう始めたらいいかな?」

Gemini: 「素晴らしい学習テーマですね!ユーザー体験の向上にも繋がります。方法はいくつか考えられますが、リアルタイム性を最優先するならSignalR、シンプルさを重視するならAPIポーリングが良いでしょう。」

技術選定、そして4つの試練

僕: 「リアルタイム性が高い方がUXは良いはずだ。まずはSignalRの実装案を見せてくれ。」

Geminiはすぐに、SignalRハブの作成、Program.csへのサービス登録、クライアントでの接続といった、実装に必要な手順とコードを提示してくれた。しかし、僕たちの前には4つの試練が待ち受けていました。

▼ 試練1:.NETのバージョン不一致
最初のコンパイルから早速エラーの洗礼を浴びた。「System.MissingMethodException」。Geminiに聞くと、プロジェクトのターゲットフレームワークとNuGetパッケージのバージョンが一致していないことが原因だと即答。僕が不注意で新しいバージョンのパッケージを参照していたのだ。バージョンを修正し、最初の関門を突破する。

▼ 試練2:HttpClient未登録
コンパイルは通った。しかし、今度は実行時エラーだ。「InvalidOperationException: ... no registered service of type 'System.Net.Http.HttpClient'」。DIコンテナへの登録が必要か。Geminiの助言通り、Program.csbuilder.Services.AddHttpClient();を追加する。基本的な「お作法」が抜けていた。

▼ 試練3:クライアントの切断
ついに処理が動き出した!…かに思えたが、バックグラウンド処理が通知を送るタイミングで、サーバーのコンソールにNullReferenceExceptionが表示され、処理がクラッシュした。

僕: 「今度はNullReferenceExceptionだよ…。あれ~~?何で何だろう??」

Gemini: 「クライアントがページを閉じるなどして、通知を送る相手が既にいなくなっている可能性があります。SendAsyncを呼び出す前に、クライアントがnullでないことを確認するチェックを追加してください。」

これは単なる設定ミスじゃない、非同期処理の難しさだ。クライアントはいつでもいなくなる可能性がある。その前提でコードを書かなければならない。

▼ 試練4:ページ離脱時の謎エラー
これで完璧なはずだった。しかし、ページを離れるタイミングで、RemoteJSRuntimeに関連する別のNullReferenceExceptionが稀に出力されることに気づいた。Geminiに聞くと、コンポーネントの破棄処理とBlazorフレームワークの処理が競合している可能性があるという。awaitを外し、「投げっぱなし」で破棄処理を開始することで、ついにすべてのエラーを乗り越えた。

戦いの果ての「戦略的選択」

画面の上を滑らかに進む青いバーを見て、僕は一度、思考をリセットするためにコーヒーを淹れた。動いたことは間違いない。しかし、一歩引いて考えてみる。

僕(心の声): 「SignalRの仕組みは、Hub、接続管理…と、複数の構成要素が連携して動いている。単体で見れば一つ一つは難しくないけど、全体としては仕組みが少し複雑だな。これを僕らのアプリに組み込むと、この機能だけが少し特殊な作りになってしまいそうだ。将来のメンテナンス性を考えると、色々と不都合が出てくるかもしれない…。」

僕は、技術的な優劣だけでなく、プロジェクト全体を見渡して判断を下した。

僕: 「Gemini、ありがとう。SignalRは動いたよ。でも、今回の要件と僕らのアプリの現状を考えると、よりシンプルで堅牢なポーリング方式の方が、現時点では最適な選択だと判断した。こちらの実装で進めよう。」

これは失敗ではない。戦略的選択だ。動くものの中から、プロジェクトにとって最も価値のある道を選ぶ。それこそがエンジニアリングの本質だ。

AIの限界、そして人間の気づき

しかし、ポーリング方式の実装中、あるエラーで僕たちは完全に手詰まりになってしまう。何度Geminiに聞いても、同じような間違った回答を繰り返すのだ。

僕(心の声): 「ハッ…!そうか、Geminiが学習したデータの中に、古い書き方(PostJsonAsync)と新しい書き方(PostAsJsonAsync)が混在していて、混乱しているんだ。これはAIには解けない。人間が気づくしかない問題だ!」

僕はGeminiの提案を無視し、自分の直感を信じてコードを修正した。そして、エラーは消えた。AIは最高の相棒だが、最後の最後で真実を見抜くのは、ディスプレイの前に座る僕たち人間の観察眼と経験なのだ

そして、ようやく動いたポーリング方式のコードを眺めているうちに、僕は次の課題に気づいてしまう。

僕: 「Gemini、この実装だと、ユーザーがページを移動したら_taskIdの情報は消えちゃうよね?つまり、進捗が追えなくなるんじゃ…?」

Gemini: 「その通りです。素晴らしい気づきです。次はその課題、状態の永続化に取り組みましょう。」

僕たちの冒険は、まだ始まったばかりだ。

次回予告

消えゆく進捗。それは、エンジニアの心の脆さか。
魂の記録を、データベースに刻むことはできるのか。
だが、黒い影がシステムを覆う。サーバー、沈黙の時。

次回、「そのタスク、サーバーが落ちても死なないの?」

Nexta Tech Blog
Nexta Tech Blog

Discussion