Communitygithub.com

Go開発パターン

堅牢で効率的かつ保守性の高いGoアプリケーションを構築するための慣用的なGoパターン、ベストプラクティス、および規約。Goコードの作成やレビュー時に、慣用的な構造や規約が疑問となる場合に使用します。

Go開発パターン とは?

このスキルは、Go開発者が慣用的なGoパターンとベストプラクティスに従って、堅牢で効率的かつ保守性の高いアプリケーションを構築するための包括的なガイドです。Goの核となる原則(シンプルさと明確さ、ゼロ値の有用化、インターフェースの受け入れと構造体の返却)を詳しく説明し、エラーハンドリング(エラーのラッピングやカスタムエラータイプ)、並行処理(ワーカープール、コンテキストによるキャンセル、グレースフルシャットダウン、errgroup、ゴルーチンリークの回避)、インターフェース設計、パッケージ構成、構造体設計、メモリとパフォーマンスの最適化など、実践的なコード例を豊富に含んでいます。このスキルは、Goコードの作成、レビュー、リファクタリング、パッケージ/モジュール設計時の意思決定を支援し、可読性が高く予測可能なコードを書くための指針を提供します。特に、Claude Code、Coder、CodexなどのAIエージェントがGoのプロジェクトで動作する際に、Goのイディオムに沿った適切な提案を行うために不可欠な知識を提供します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/golang-patterns

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Go開発パターン は何をしますか?

堅牢で効率的かつ保守性の高いアプリケーションを構築するための、慣用的なGoパターンとベストプラクティス。

使用タイミング

  • 新しいGoコードを作成するとき
  • Goコードをレビューするとき
  • 既存のGoコードをリファクタリングするとき
  • Goのパッケージ/モジュールを設計するとき

核となる原則

1. シンプルさと明確さ

Goは、賢さよりもシンプルさを重視します。コードは明白で読みやすくなければなりません。

// 良い例: 明確で直接的なコード
func GetUser(id string) (*User, error) {
    user, err := db.FindUser(id)
    if err != nil {
        return nil, fmt.Errorf("get user %s: %w", id, err)
    }
    return user, nil
}

// 悪い例: 過度に賢いコード
func GetUser(id string) (*User, error) {
    return func() (*User, error) {
        if u, e := db.FindUser(id); e == nil {
            return u, nil
        } else {
            return nil, e
        }
    }()
}

2. ゼロ値を有用にする

初期化なしですぐに使用できるように型を設計します。

// 良い例: ゼロ値が有用
  type Counter struct {
    mu    sync.Mutex
    count int // ゼロ値は0で、すぐに使用可能
}

func (c *Counter) Inc() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}

// 良い例: bytes.Bufferはゼロ値で動作する
var buf bytes.Buffer
buf.WriteString("hello")

// 悪い例: 初期化が必要
  type BadCounter struct {
    counts map[string]int // nilマップはパニックを引き起こす
}

3. インターフェースを受け入れ、構造体を返す

関数はインターフェースのパラメータを受け入れ、具象型を返すようにします。

// 良い例: インターフェースを受け入れ、具象型を返す
  func ProcessData(r io.Reader) (*Result, error) {
    data, err := io.ReadAll(r)
    if err != nil {
        return nil, err
    }
    return &Result{Data: data}, nil
}

// 悪い例: インターフェースを返す(不必要に実装の詳細を隠す)
  func ProcessData(r io.Reader) (io.Reader, error) {
    // ...
}

エラーハンドリングパターン

コンテキスト付きエラーラッピング

// 良い例: コンテキストでエラーをラップする
  func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("load config %s: %w", path, err)
    }

    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, fmt.Errorf("parse config %s: %w", path, err)
    }

    return &cfg, nil
}

カスタムエラータイプ

// ドメイン固有のエラーを定義する
  type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

// 一般的なケースに対するセンチネルエラー
var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrInvalidInput = errors.New("invalid input")
)

errors.Is と errors.As を使ったエラーチェック

func HandleError(err error) {
    // 特定のエラーをチェック
    if errors.Is(err, sql.ErrNoRows) {
        log.Println("No records found")
        return
    }

    // エラータイプをチェック
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        log.Printf("Validation error on field %s: %s",
            validationErr.Field, validationErr.Message)
        return
    }

    // 未知のエラー
    log.Printf("Unexpected error: %v", err)
}

エラーを決して無視しない

// 悪い例: ブランク識別子でエラーを無視する
result, _ := doSomething()

// 良い例: エラーを処理するか、無視しても安全な理由を明示的に文書化する
  result, err := doSomething()
if err != nil {
    return err
}

// 許容されるケース: エラーが本当に問題にならない場合(稀)
  _ = writer.Close() // ベストエフォートのクリーンアップ、エラーは別途記録済み

並行処理パターン

ワーカープール

func WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for job := range jobs {
                results <- process(job)
            }
        }()
    }

    wg.Wait()
    close(results)
}

キャンセルとタイムアウトのためのコンテキスト

func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("fetch %s: %w", url, err)
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}

グレースフルシャットダウン

func GracefulShutdown(server *http.Server) {
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    <-quit
    log.Println("Shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("Server forced to shutdown: %v", err)
    }

    log.Println("Server exited")
}

協調的なゴルーチンのための errgroup

import "golang.org/x/sync/errgroup"

func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
    g, ctx := errgroup.WithContext(ctx)
    results := make([][]byte, len(urls))

    for i, url := range urls {
        i, url := i, url // ループ変数をキャプチャ
          g.Go(func() error {
            data, err := FetchWithTimeout(ctx, url)
            if err != nil {
                return err
            }
            results[i] = data
            return nil
        })
    }

    if err := g.Wait(); err != nil {
        return nil, err
    }
    return results, nil
}

ゴルーチンリークの回避

// 悪い例: コンテキストがキャンセルされるとゴルーチンリークが発生する
  func leakyFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte)
    go func() {
        data, _ := fetch(url)
        ch <- data // レシーバーがなければ永久にブロック
    }()
    return ch
}

// 良い例: キャンセルを適切に処理する
  func safeFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte, 1) // バッファ付きチャネル
    go func() {
        data, err := fetch(url)
        if err != nil {
            return
        }
        select {
        case ch <- data:
        case <-ctx.Done():
        }
    }()
    return ch
}

インターフェース設計

小さく、焦点を絞ったインターフェース

// 良い例: 単一メソッドのインターフェース
  type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// 必要に応じてインターフェースを合成する
  type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

インターフェースは使用する側で定義する

// プロバイダー側ではなく、コンシューマー側のパッケージで
  package service

// UserStoreはこのサービスが必要とするものを定義する
  type UserStore interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

type Service struct {
    store UserStore
}

// 具象実装は別のパッケージに置くことができる
// このインターフェースについて知る必要はない

型アサーションによるオプショナルな振る舞い

type Flusher interface {
    Flush() error
}

func WriteAndFlush(w io.Writer, data []byte) error {
    if _, err := w.Write(data); err != nil {
        return err
    }

    // サポートされていればFlushする
      if f, ok := w.(Flusher); ok {
        return f.Flush()
    }
    return nil
}

パッケージ構成

標準的なプロジェクトレイアウト

myproject/
├── cmd/
│   └── myapp/
│       └── main.go           # エントリポイント
├── internal/
│   ├── handler/              # HTTPハンドラ
│   ├── service/              # ビジネスロジック
│   ├── repository/           # データアクセス
│   └── config/               # 設定
├── pkg/
│   └── client/               # 公開APIクライアント
├── api/
│   └── v1/                   # API定義(proto, OpenAPI)
├── testdata/                 # テストフィクスチャ
├── go.mod
├── go.sum
└── Makefile

パッケージの命名

// 良い例: 短く、小文字、アンダースコアなし
  package http
package json
package user

// 悪い例: 冗長、大文字小文字混在、または冗長なもの
  package httpHandler
package json_parser
package userService // 冗長な 'Service' 接尾辞

パッケージレベルの状態を避ける

// 悪い例: グローバルな可変状態
var db *sql.DB

func init() {
    db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
}

// 良い例: 依存性注入
  type Server struct {
    db *sql.DB
}

func NewServer(db *sql.DB) *Server {
    return &Server{db: db}
}

構造体設計

関数型オプションパターン

type Server struct {
    addr    string
    timeout time.Duration
    logger  *log.Logger
}

type Option func(*Server)

func WithTimeout(d time.Duration) Option {
    return func(s *Server) {
        s.timeout = d
    }
}

func WithLogger(l *log.Logger) Option {
    return func(s *Server) {
        s.logger = l
    }
}

func NewServer(addr string, opts ...Option) *Server {
    s := &Server{
        addr:    addr,
        timeout: 30 * time.Second, // デフォルト
        logger:  log.Default(),    // デフォルト
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// 使用例
  server := NewServer(":8080",
    WithTimeout(60*time.Second),
    WithLogger(customLogger),
)

コンポジションのための埋め込み

type Logger struct {
    prefix string
}

func (l *Logger) Log(msg string) {
    fmt.Printf("[%s] %s\n", l.prefix, msg)
}

type Server struct {
    *Logger // 埋め込み - ServerはLogメソッドを獲得
    addr    string
}

func NewServer(addr string) *Server {
    return &Server{
        Logger: &Logger{prefix: "SERVER"},
        addr:   addr,
    }
}

// 使用例
  s := NewServer(":8080")
s.Log("Starting...") // 埋め込まれたLogger.Logを呼び出す

メモリとパフォーマンス

サイズがわかっている場合はスライスを事前割り当てする

// 悪い例: スライスが複数回拡張される
  func processItems(items []Item) []Result {
    var results []Result
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

// 良い例: 単一の割り当て
  func processItems(items []Item) []Result {
    results := make([]Result, 0, len(items))
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

頻繁な割り当てには sync.Pool を使用する

var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func ProcessRequest(data []byte) []byte {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()

    buf.Write(data)
    // 処理...
      return buf.Bytes()
}

ループ内での文字列連結を避ける

// 悪い例: 多くの文字列割り当てが発生する
  func join(parts []string) string {
    var result string
    for _, p := range parts {
        result += p + ","
    }
    return result
}

// 良い例: strings.Builder で単一の割り当て
  func join(parts []string) string {
    var sb strings.Builder
    for i, p := range parts {
        if i > 0 {
            sb.WriteString(",")
        }
        sb.WriteString(p)
    }
    return sb.String()
}

// 最良の例: 標準ライブラリを使用する
  func join(parts []string) string {
    return strings.Join(parts, ",")
}

Goツールの統合

必須コマンド

# ビルドと実行
  go build ./...
go run ./cmd/myapp

# テスト
  go test ./...
go test -race ./...
go test -cover ./...

# 静的解析
  go vet ./...
staticcheck ./...
golangci-lint run

# モジュール管理
  go mod tidy
go mod verify

# フォーマット
  gofmt -w .
goimports -w .

推奨リンター設定 (.golangci.yml)

linters:
  enable:
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - unused
    - gofmt
    - goimports
    - misspell
    - unconvert
    - unparam

linters-settings:
  errcheck:
    check-type-assertions: true
  govet:
    enable:
      - shadow

issues:
  exclude-use-default: false

クイックリファレンス: Goのイディオム

イディオム説明
インターフェースを受け入れ、構造体を返す関数はインターフェースパラメータを受け入れ、具象型を返す
エラーは値であるエラーを例外ではなく、第一級の値として扱う
メモリを共有することで通信しないゴルーチン間の調整にはチャネルを使用する
ゼロ値を有用にする型は明示的な初期化なしで動作するべき
ちょっとしたコピーは、ちょっとした依存関係よりマシ不必要な外部依存関係を避ける
明確さは賢さに勝る賢さよりも可読性を優先する
gofmtは誰の好みでもないが、みんなの友達常に gofmt/goimports でフォーマットする
早期リターンエラーを最初に処理し、ハッピーパスはインデントしない

避けるべきアンチパターン

// 悪い例: 長い関数での裸のreturn
  func process() (result int, err error) {
    // ... 50行 ...
      return // 何が返されているのか?
}

// 悪い例: 制御フローにpanicを使用する
  func GetUser(id string) *User {
    user, err := db.Find(id)
    if err != nil {
        panic(err) // これはしてはいけない
    }
    return user
}

// 悪い例: 構造体にコンテキストを渡す
  type Request struct {
    ctx context.Context // コンテキストは最初のパラメータにするべき
    ID  string
}

// 良い例: コンテキストを最初のパラメータとして
  func ProcessRequest(ctx context.Context, id string) error {
    // ...
}

// 悪い例: 値レシーバとポインタレシーバの混在
  type Counter struct{ n int }
func (c Counter) Value() int { return c.n }    // 値レシーバ
func (c *Counter) Increment() { c.n++ }        // ポインタレシーバ
// スタイルを一つ選び、一貫性を保つ

覚えておいてください: Goのコードは、最高の意味で退屈であるべきです。予測可能で、一貫性があり、理解しやすいものにしましょう。迷ったときは、シンプルに保ちましょう。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

関連スキル