go について
Go のアプリケーションやモジュール開発に関するガイドです。作者の規約に沿って作業するコーディングエージェント向けに、構成、依存性注入、テスト、Lint、ドキュメントを説明します。
主な機能
- 01アプリケーションとパッケージの構成
- 02依存性注入の規約
- 03実際の依存関係を使ったテスト
- 04テストとLintのコマンド
- 05Goのドキュメント規約
活用シーン
- 01Go製Webアプリの構成を決める
- 02データベース関連のパッケージテストを書く
- 03プロジェクトのテストとLintを実行する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ maragudk/ skills --skill goインストール後、Claude Code で/goを入力して読み込まれたか確認します。
詳細
Go
Go でアプリケーションやモジュール/ライブラリを開発する方法を説明するガイドです。
一部はアプリケーションにのみ当てはまり、ほかのプロジェクトでライブラリとして使われるモジュールには適用されません。たとえば、データベースへのアクセスやサーバーの実行などです。
アプリケーションの構成
通常、Web アプリケーションやライブラリ/モジュールを作成します。
アプリケーションには、一般に次のパッケージが含まれます(一部がない場合、通常はそのプロジェクトで必要ないということです)。
main:アプリケーションのメインエントリーポイントを含みます(ディレクトリcmd/app)。model:ほかのパッケージ全体で使われるドメインモデルを含みます。sql/sqlite/postgres:SQL データベース関連のロジック、データベースのマイグレーション(サブディレクトリmigrations/内)、テストフィクスチャ(サブディレクトリtestdata/fixtures/内)を含みます。使用するデータベースは SQLite または PostgreSQL です。sqltest/sqlitetest/postgrestest:テスト用データベースのセットアップと後片付けに使うパッケージです。s3:Amazon S3 または互換性のあるオブジェクトストアとやり取りするロジックです。s3test:テスト用 S3 バケットのセットアップと後片付けに使うパッケージです。llm:大規模言語モデル(LLM)や基盤モデルとやり取りするクライアントです。llmtest:テスト用の LLM クライアントをセットアップするために使うパッケージです。http:アプリケーションの HTTP ハンドラーです。html:アプリケーションの HTML テンプレートです。gomponents ライブラリを使って記述します(使い方が必要な場合は https://www.gomponents.com/llms.txt を参照してください)。
コードスタイル
依存性注入
コンポーネント間の依存性注入を積極的に利用します。通常、受け取り側に非公開インターフェースを定義して行います。この例では userGetter の使い方に注目してください。
package http
import (
"net/http"
"github.com/go-chi/chi/v5"
"maragu.dev/httph"
"model"
)
type UserResponse struct {
Name string
}
type userGetter interface {
GetUser(ctx context.Context, id model.ID) (model.User, error)
}
func User(r chi.Router, db userGetter) {
r.Get("/user", httph.JSONHandler(func(w http.ResponseWriter, r *http.Request, _ any) (UserResponse, error) {
id := r.URL.Query().Get("id")
user, err := db.GetUser(r.Context(), model.ID(id))
if err != nil {
return UserResponse{}, httph.HTTPError{Code: http.StatusInternalServerError, Err: errors.New("error getting user")}
}
return UserResponse{Name: user.Name}, nil
}))
}
テスト
ほとんどの関数やメソッドにテストを書きます。何が行われているか、期待される結果は何かを適切に説明するサブテストをほぼ常に使います。
例を示します。
package example
type Thing struct {}
func (t *Thing) DoSomething() (bool, error) {
return true, nil
}
package example_test
import (
"testing"
"maragu.dev/is"
"example"
)
func TestThing_DoSomething(t *testing.T) {
t.Run("should do something and return a nil error", func(t *testing.T) {
thing := &example.Thing{}
ok, err := thing.DoSomething()
is.NotError(t, err)
is.True(t, ok)
})
}
テーブル駆動テストを使うこともあります。
package example
import "errors"
type Thing struct {}
var ErrChairNotSupported = errors.New("chairs not supported")
func (t *Thing) DoSomething(with string) error {
if with == "chair" {
return ErrChairNotSupported
}
return nil
}
package example_test
import (
"testing"
"maragu.dev/is"
"example"
)
func TestThing_DoSomething(t *testing.T) {
tests := []struct {
name string
input string
expected error
}{
{name: "should do something with the table and return a nil error", input: "table", expected: nil},
{name: "should do something with the chair and return an ErrChairNotSupported", input: "chair", expected: example.ErrChairNotSupported},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
thing := &example.Thing{}
err := thing.DoSomething(test.input)
if test.expected != nil {
is.Error(t, test.expected, err)
} else {
is.NotError(t, err)
}
})
}
}
モックよりも、実際の依存関係を使った統合テストを優先します。本物に勝るものはありません。依存関係は通常、Docker コンテナーで実行します。テストの実行時には、依存関係が稼働しているものとしてかまいません。
テストで重要なのが依存関係そのものではなく、依存関係の役割が小さい場合は、モックを使うのが適切です。ただし、たとえばデータベースのメソッドをテストする場合は、実際のデータベースを使ってください。
アサーションには maragu.dev/is モジュールを使います。利用可能な関数は is.True、is.Equal、is.Nil、is.NotNil、is.EqualSlice、is.NotError、is.Error です。いずれも最後の引数に省略可能なメッセージを受け取ります。
テストはシャッフルされるため、サブテストであってもテストの実行順序に依存しないでください。
postgrestest.NewDatabase(t)/sqlitetest.NewDatabase(t) のテストヘルパーを呼び出すたびに、データベースはクリーンな状態になります(ほかのテストの残りなどはありません)。
テストではデータベースフィクスチャを使えます。複数のテストで同じデータ、または非常によく似たデータを使う場合は、テストごとに同じデータをセットアップせずに済むよう、フィクスチャを優先してください。フィクスチャは sqlite/testdata/fixtures/postgres/testdata/fixtures にあります。sqlitetest.NewDatabase(t, sqlitetest.WithFixtures("fixture one", "fixture two")) のように使います。フィクスチャは指定した順に適用されます。
テストヘルパー関数では testing.T.Helper() を呼び出してください。
テストでは context.Background() の代わりに t.Context() を使い、ctx 変数に取り出さず、必ずインラインで使ってください。
その他
- 変数の命名:
- リクエストには
req、レスポンスにはresを使用します
- リクエストには
- SQL ヘルパーは
Database.H.Select、Database.H.Exec、Database.H.Get、Database.H.InTxで利用できます。 - Go では
interface{}の代わりに組み込みのanyを使用します。 - 標準ライブラリの
sql.ErrNoRowsのエイリアスがmaragu.dev/glue/sql.ErrNoRowsにあるため、両方をインポートする必要はありません。 - すべての HTML ボタンに
cursor-pointerCSS クラスを付けてください。 - SQLite の時刻形式は、常に
strftime('%Y-%m-%dT%H:%M:%fZ')が返す文字列です。データベースを扱う際は、標準ライブラリのtime.Timeではなくmaragu.dev/glue/model.Time(通常、プロジェクトではmodel.Timeとしてエイリアスされています)を使用してください。 - Go のプライベート関数はパッケージレベルなので、同じパッケージ内の別ファイルからも使用できます。
- ドキュメントは、識別子名を文の最初の語にし、その後は同じことを繰り返さずに文を完成させるという Go のスタイルに従ってください。例:「// SearchProducts using the given search query and result limit.」は適切ですが、「// SearchProducts searches products using the given search query and result label.」は適切ではありません。
- パッケージレベルの識別子は、他のパッケージに公開する API の範囲を小さくするため、デフォルトで小文字から始め、パッケージ内だけで参照できるようにしてください。
- 任意の値を文字列に変換する場合は、
strconvの関数ではなくfmt.Sprintを使用してください。 - リテラルへのポインターを作成する場合は、ポインター関数ではなく(Go 1.26 以降で利用できる)
new()を使用してください。
テスト、リンティング、評価
すべてのテストを実行するには、make test または go test -shuffle on ./... を実行してください。1 つのパッケージのテストだけを実行するには、go test -shuffle on ./path/to/package を実行してください。特定のテストを実行するには、go test ./path/to/package -run TestName を実行してください。
リンターを実行するには、make lint または golangci-lint run を実行してください。リンターは常にパッケージまたはディレクトリ単位で実行する必要があります。単一ファイルを指定しても動作しません。
LLM の評価を実行するには、make eval または go test -shuffle on -run TestEval ./... を実行してください。
プロジェクト内のすべてのコードをフォーマットするには make fmt を実行してください。最後の仕上げに便利です。
シェルで psql または sqlite3 を使用してデータベースにアクセスできます。
ドキュメント
Go モジュールのドキュメントは、通常、モジュール名を指定して go doc を実行すると確認できます。たとえば、標準ライブラリのモジュールなら go doc net/http、サードパーティのモジュールなら go doc maragu.dev/gai を実行します。ChatCompleter インターフェースのような識別子の詳細なドキュメントは、go doc maragu.dev/gai.ChatCompleter のようにして確認できます。
ブラウザーでアプリを確認する
Chrome Dev Tools MCP ツールを使用して、アプリが実行中で、ブラウザーから利用できる状態だと想定できます。コードを変更すると自動的に再読み込みされるため、手動で再読み込みする必要はありません。
実行中のアプリケーションのログ出力は、プロジェクトのルートにある app.log に記録されます。