Goで一晩かけてVibesじゃなくちゃんとAIを導いてみた
これまでの AI Coding では人間が Pilot (主) で AI が Copilot (補助) でした。
これからの Agentic Coding ではその関係が逆転し、人間が補助役として AI に文脈を与え、正しい方向へ導く能力が必要になります。
夜なべのきっかけ
X のホームのおすすめ機能は良い塩梅にレコメンデーションしてくれてるなと、一ユーザーとして高く評価しています。開発系や AI 関連のリストも管理していますが、見る機会がないほどです。
昨日もなんとなく眺めてたらトランザクション・アウトボックス・パターン(Transactional Outbox Pattern)という設計があることを知りました。
トランザクション・アウトボックス・パターンとは
マイクロサービス開発では何らかの action を契機として、event を発行し、別システムが event を元に処理するという疎結合な仕組みがよく使われます。
例えばユーザー登録という action 後に、UserCreated という event を発行し、別のメール送信システムが event を受け取り次第「Welcome メール」を送信する、といった具合です。
イベント駆動アーキテクチャ(EDA)という名前がついています。
トランザクション・アウトボックス・パターンは EDA を補完する設計パターンで、ユーザー登録の書き込みと、イベント送信するためのアウトボックス(送信箱)への書き込みを同一トランザクション内で行うことで、イベントの損失を防ぎ、整合性を保証します。
元ツイートは DynamoDB の CDC(変更データキャプチャ)を使うことでよりシンプルな構成で実現する方式ですね。
Agentic Coding 以前の世界線では「なるほどなぁ」で終わっていましたが、今なら適切な技術的意思決定をした上で、ある程度のコードをサクッと書けるなと思い立ったので動くものを作ってみました。
Go で書いた完成品のサンプルコードがこちらです。
README.md と CLAUDE.md を見れば概要わかると思います。ディレクトリ構成はコード量が少ないことが見込まれたので DDD ではなくシンプルにしています。
技術スタック
習得済の技術と未使用の技術では作業完了までの時間が 2〜10 倍は変わってくるはずなので、今回採用した技術スタックを前提情報として記しておきます。
既知の技術
- Go 言語: 実務経験5年以上
- Docker / docker-compose: 開発環境の範囲で迷うことはない
- PostgreSQL: サンプルコードなので SQL 文法以外は意識することがない
- golang-migrate: 勝手はわかってるがコマンドとか忘れてる状態
- イベント駆動アーキテクチャ: 実務でよくある
未知の技術
- sqlc: 時雨堂 V さんの技術選定をよく Watch してるので注目してた
- Redis Streams: 実務で使う機会はなさそうだがイベント送信の通知用としてローカル環境で用意しやすいので選択
- golangci-lint v2: どの現場でも必ず使うが常に忘却している
- rueidis: redis/go-redis が有名だけどより高速なものができてた(台湾の rueian さんが作者)
- slog: Go 標準の構造化ロガーだけど uber-go/zap か rs/zerolog ばかりで使う機会なかった
人間と AI の開発速度
AI と人間の開発効率が同じくらいなこと
- golangci-lint のルール選定
- Lint エラーの修正
そもそも Lint ルール導入の意思決定を行うのが人間なのと、出力されるエラー内容の把握は人間の認知能力で十分なため、あまり変わりません。
ちなみに実装時間というか、人間の検討時間の半分以上は golangci-lint v2 の仕様確認とルール選定に費やしました。
AI が人間よりも数倍〜数十倍速いと実感したこと
-
設計: アーキテクチャの検討、Pros/Cons、前提知識の習得
- 数時間 or 数日かけて理解していたものが、知識の渋滞を抜けて高速道路を走れるように
- テーブル設計: できたものに実務経験のエッセンスを+αするだけ
- Redis Streams の仕様理解: 従来は技術記事探しに一苦労、不足情報は公式ドキュメントを読み込んで自分で検証…、AI に任せれば数時間かかっていたものが 10〜15 分で把握可能に
- Mermaid によるシーケンス図作成: 人間に向いた作業ではない。1-2時間の作業が手直し含めて5分に
ここに至るまでの苦労
1ヶ月以上 Claude Code をやりこんできて、ようやく AI 駆動開発に必要なドキュメントや手順のような『型』が見えてきたところです。それ以前は試行錯誤と検証の連続でたくさん時間を使いました。軌道に乗るためにはプロンプトが手に馴染み、頭の中で指示と文脈が構造化されるまでの、長い学習期間が必要でした。遠回りして地道に踏み固めた先で一気に開花する感じでした。
おまけ: AI の Go 言語 🐣 コード集
- ディレクトリ名が複数形になる
- Web 系の慣習だと複数形にしがちですが Go はディレクトリ名=パッケージ名なので、単数系で書き、概念として扱うべきです
- go.mod を手動修正したがる
-
go getとgo mod tidyさせます
-
- interface{} や slog.Any のような型情報のないものを使って楽したがる
- こっそり Lint ルール削除
- 構造化ログで型付き引数ではなく型なし引数を使う
// not good
slog.Info("message", "key", value, "key2", value2)
// good
slog.Info("message",
slog.String("key", "value"),
slog.Int("count", 42),
slog.Duration("elapsed", time.Second),
)
注意すべきアンチパターン
AI の作業にツッコミを入れて軌道修正してあげるととても良いコードを書いてくれます。しかし AI が生成したコードに疑問を抱かず、そのまま受け入れて PullRequest を作成してしまうと、レビューするチームメンバーが疲弊するのは目に見えています。結局レビューしきれずササッとマージして技術的負債が増えていくのだろうなというのも想像に難くないです。
技術的負債を Issue 駆動の Claude Code GitHub Actions で自動リファクタリングさせていくのが良いのか、あるいは技術的負債を能動的に制御できる技術レベルのメンバーだけで構成した超少数チームの方が結果的に開発生産性が高くなるのか…。緩やかに後者になっていくのではと考えています。
ちなみに今回の実装は一晩で終わりましたが、その後にこの記事がいい感じにまとまるまで 2 日を要しました(人間ボトルネックすぎる)。
Discussion