Claude Media
Claude CodeでGoアプリを開発する手順 — CLAUDE.mdとhooksの設定例

Claude CodeでGoアプリを開発する手順 — CLAUDE.mdとhooksの設定例

Claude CodeにGoアプリを作らせるときの、go mod initからgo test・-race・クロスコンパイルまでの検証ループと、CLAUDE.md・hooksの設定例をまとめます。

Goの開発をClaude Codeに任せるときの要点

Goは検証コマンドが標準ツールに揃っている言語です。go build、go test、go vet、gofmt を回せば、Claude Codeが書いたコードの良し悪しをその場で判定できます。任せ方の要は、この検証コマンドを CLAUDE.md に書いておき、変更のたびに実行させて出力を読ませることです。

この記事では次の流れで進めます。

  1. go mod init でモジュールを作り、cmd/ と internal/ に分ける
  2. CLAUDE.md に規約と検証コマンドを書く
  3. hookで gofmt を自動実行する
  4. go test と -race の結果を読ませて直させる
  5. GOOS と GOARCH でクロスコンパイルを確認する

Claude Codeのインストールや基本操作は済んでいる前提です。同じ型でWebフレームワークを扱う例はClaude CodeでDjangoアプリをMVT構成で開発する手順にあります。

モジュールを作りディレクトリを分ける

前提として、Goのチュートリアルは、ある程度のプログラミング経験、コードを編集するテキストエディター、コマンドの端末があれば始められるとしています。エディターはVSCode(無料)、GoLand(有料)、Vim(無料)が代表例です。Goのインストールは公式サイトのダウンロード手順に従います。Claude Codeを使う場合も、go version が通る状態までは自分で整えておきます。

Goの公式チュートリアルは、空のディレクトリで go mod init を実行するところから始まります。go.mod は依存関係を追跡するファイルで、コードと一緒にリポジトリへ置きます。モジュールパスは、公開するならGoツールがダウンロードできる場所(リポジトリのURL)にします。

mkdir myapp && cd myapp
go mod init github.com/example/myapp

最初の動作確認は go run . です。チュートリアルでは main パッケージに fmt.Println("Hello, World!") を書いた hello.go をこのコマンドで実行します。go run は数あるgoコマンドの1つで、go help で一覧を確認できます。Claude Codeに「動く最小構成を作って go run . で確かめて」と頼めば、実行結果まで読んで報告してくれるので、環境の不備も早い段階で見つかります。

モジュールレイアウトの解説では、コマンドが補助パッケージを持つ場合は internal/ にまとめるのが推奨です。internal/ 配下のパッケージは他のモジュールからimportできないため、APIを公開せずに自由にリファクタリングできます。コマンドが複数あるリポジトリでは cmd/ の下に置くのが一般的な慣習です。

myapp/
  go.mod
  cmd/
    myapp/
      main.go
  internal/
    store/
      store.go
      store_test.go

Claude Codeに「新しいパッケージを作って」と頼むと、置き場所をその場で決めます。最初にこの構成を明示しておくと、公開したくないコードが根に散らばるのを防げます。

CLAUDE.mdに書く規約と検証コマンド

CLAUDE.md はセッション開始時に毎回読み込まれる指示です。メモリのドキュメントでは、具体的で簡潔な指示ほど守られやすく、1ファイル200行以内が目安とされています。ただしClaudeはこれを文脈として読むだけで、強制設定ではありません。必ず守らせたい動作は、後述のhookで担保します。

Go向けの例を示します。内容はEffective Goの記述に沿っています。

# CLAUDE.md
## 構成
- 実行ファイルは cmd/<name>/main.go、共有ロジックは internal/ に置く
## コーディング規約
- コードは gofmt の出力を正とする(手で整形しない)
- エラーの戻り値は必ず確認する。`_` で捨てない
- goroutine を起動する関数は、終了と失敗を呼び出し側に返す
## 検証(変更のたびに実行)
- go build ./...
- go vet ./...
- go test ./...
- 並行処理に触れたら go test -race ./...

規約の根拠はEffective Goにあります。Effective Goは、エラーを捨てるために戻り値を無視するのを「terrible practice(ひどい慣行)」と呼び、エラーの戻り値は必ず確認するよう述べています。整形については、書き方に迷ったら gofmt を実行し、出力が期待と違うならプログラムの側を組み替えるよう説明しています。

CLAUDE.md の置き場所は用途で分かれます。チームで共有する規約はプロジェクト直下、自分だけの環境設定は CLAUDE.local.md に書きます。後者は .gitignore に入れる想定です。役割分担の考え方はCLAUDE.mdとSkillsの役割分担で詳しく扱っています。

hookでgofmtを必ず走らせる

CLAUDE.md の指示は文脈にすぎません。整形のように毎回機械的に済ませたい処理は、hookに任せるほうが確実です。hooksガイドには、ファイル編集後にPrettierを実行する例があります。PostToolUse イベントに Edit|Write のmatcherを指定し、編集されたファイルのパスをコマンドへ渡す構成です。

同じ型でGo用に組み替えると、次のようになります(hooksガイドの例に沿った形で、Go向けの書き換えは本記事の例示です)。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | grep '\\.go$' | xargs -r gofmt -w"
          }
        ]
      }
    ]
  }
}

これを .claude/settings.json に置きます。jq が必要です。grep で .go ファイルだけに絞っているのは、編集対象がGo以外のときに gofmt が失敗しないようにするためです。hookが成功しても会話には何も表示されません。編集したファイルが整形されているかで動作を確認します。

もう一段強く縛りたいときは PreToolUse のhookでexit code 2を返す方法があります。hooksガイドではexit 2が「アクションをブロックする」動作で、stderrに書いた理由がClaudeへのフィードバックになると説明されています。go.sum の手編集を止めるような用途に向きます。

命名とリソース解放の規約も書いておく

Claude Codeは他言語の癖でGoを書くことがあります。Effective Goが挙げる慣習のうち、レビューで指摘されやすいものを CLAUDE.md に足しておくと、手戻りが減ります。

  • パッケージ名は小文字の1語にし、アンダースコアや mixedCaps を使わない
  • フィールド owner のgetterは GetOwner でなく Owner とし、setterは SetOwner とする
  • 複数語の名前は MixedCaps か mixedCaps で書き、アンダースコアで区切らない
  • ファイルなどを開いたら、直後に defer f.Close() と書く

Effective Goによると、Close のような呼び出しを defer で予約すると利点が2つあります。1つは閉じ忘れを防げること、もう1つは関数のどの出口でも確実に実行されることです。

回復できないエラーには組み込み関数 panic があります。ただし通常のエラー報告は、戻り値に error を足して返す方法が基本です。Claudeが安易に panic を使っていたら、戻り値に直させます。

テストを書かせて結果を読ませる

Goでは _test.go で終わるファイル名にテスト関数を書きます。関数名は Test で始め、引数に *testing.T を取ります。公式チュートリアルは t.Errorf で失敗を報告する形です。

package store
 
import "testing"
 
func TestGetMissing(t *testing.T) {
    s := New()
    if _, err := s.Get("nope"); err == nil {
        t.Errorf("Get(%q) = nil error, want error", "nope")
    }
}

実行は go test です。-v を付けると、テストの一覧と結果が出ます。

go test ./...
go test -v ./internal/store

Claude Codeへの頼み方はこうです。

internal/store に Get を実装して。先にテストを書き、go test ./... を実行して失敗を確認してから実装し、最後に全部通ることを確認して。

テストが通るまでの反復は、Claude Codeが得意とする作業です。出力に失敗メッセージが出れば、それを読んで直します。人間の役目は、テストが仕様を表しているかの確認です。テストが緩ければ、通っても意味がありません。

goroutineとエラー処理を任せるときの落とし穴

Effective Goは並行処理について、共有した値をチャネルで受け渡すことで、値に触れるgoroutineを同時に1つに保つ考え方を紹介しています。標語は「メモリを共有して通信するな。通信してメモリを共有せよ」です。この方針に従えば、データ競合は設計上起きないと説明されています。

一方、goroutineの中で起きたエラーは、呼び出し元に自然には届きません。Claude Codeに並行処理を書かせるときは、CLAUDE.md に書いた「終了と失敗を呼び出し側に返す」を具体的に指示すると安定します。例えば、次のような形です。

func fetchAll(urls []string) error {
    errCh := make(chan error, len(urls))
    for _, u := range urls {
        go func(u string) {
            errCh <- fetch(u)
        }(u)
    }
    var firstErr error
    for range urls {
        if err := <-errCh; err != nil && firstErr == nil {
            firstErr = err
        }
    }
    return firstErr
}

バッファ付きチャネルに全件のエラーを流し、呼び出し側で回収する最小の形です。fetch は例示用の関数です。

並行処理を含む変更では、検証にレース検出器を加えます。レース検出器の解説では、go test・go run・go build・go install のいずれにも -race を付けられます。

go test -race ./...

レース検出器の出力を読ませ、競合の箇所を直させる流れを反復すると、レビューの負担が減ります。CLAUDE.md の検証欄に「並行処理に触れたら -race」と書いたのはこのためです。

GOOSとGOARCHでクロスコンパイルを確認する

Goは環境変数 GOOS(対象OS)と GOARCH(対象アーキテクチャ)を指定するだけで、別環境向けの実行ファイルを作れます。GOOS には linux、darwin、windows などが、GOARCH には amd64 や arm64 などが選べます。

GOOS=linux GOARCH=arm64 go build -o dist/myapp-linux-arm64 ./cmd/myapp
GOOS=windows GOARCH=amd64 go build -o dist/myapp.exe ./cmd/myapp

有効な組み合わせは限られています。Claude Codeに配布物の作成を任せるときは、組み合わせを自分で決めて指示してください。ビルドが通ることまでは自動で確認できますが、対象環境での動作確認は別に必要です。

任せる範囲の目安

作業Claude Codeに任せる理由
関数・パッケージの実装Claude Codeに任せる◎理由go build と go vet で即座に検証できる
テストの追加と修正の反復Claude Codeに任せる◎理由go test の出力が判断材料になる
goroutineを含む変更Claude Codeに任せる○理由-race を通しても、設計の妥当性は自分で確認する
モジュールパスの決定Claude Codeに任せる△理由公開URLと一致させる必要があり、自分で決める
配布用バイナリの動作保証Claude Codeに任せる△理由ビルドは通っても、対象環境での実機確認が必要

よくあるつまずき

依存を足したのにgo buildが通らない

外部パッケージをimportしたのに解決されないときは、go mod tidy で go.mod を更新させます。GoのWebサービスのチュートリアルも、この手順を案内しています。

Claudeが手で整形して差分が増える

CLAUDE.md に「gofmtを正とする」と書き、hookで gofmt -w を実行しておけば、整形差分でレビューが荒れることはありません。整形はClaudeの判断に任せず、ツールに任せます。

エラーを握りつぶすコードが混じる

CLAUDE.md の規約に加え、レビューの依頼文に「エラーの戻り値が _ で捨てられていないか確認して」と書き添えます。Effective Goが「必ず確認する」と述べている点なので、根拠を示して指示できます。

まとめ

Goの開発は、検証コマンドの出力をClaude Codeに読ませる反復とよく噛み合います。CLAUDE.md に構成と検証コマンドを書き、gofmt はhookで自動化し、並行処理には -race を加える。この3点を最初に整えておくと、任せられる範囲が広がります。

GoでMCPサーバーを作る場合はGoでMCPサーバーを書く公式SDKの使い方、Claude APIをGoから呼ぶ場合はClaude Go SDKの実装ガイドが続きの手順です。

この記事を共有:XはてブLinkedIn