Communitygithub.com

Go開発パターン

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

Go開発パターン とは?

このスキルは、Go開発者向けの包括的なベストプラクティス集であり、堅牢で効率的かつ保守しやすいアプリケーションを構築するための慣用的なパターンを提供します。単純明快さの重視、ゼロ値の有効活用、「インターフェースを受け入れ、構造体を返す」原則など、Goの中核的な設計思想を網羅しています。具体的には、エラーラッピングやカスタムエラー型を使ったエラーハンドリング、ワーカープールやコンテキストによるタイムアウト処理といった並行処理パターン、小さく焦点を絞ったインターフェース設計、機能的オプションパターンによる構造体設計、パッケージ構成の標準レイアウト、メモリとパフォーマンスの最適化テクニックなど、実践的なコード例とともに解説します。このスキルは、Claude CodeやCursorなどのAIエージェント上でGoコードの記述、レビュー、リファクタリングを支援するために設計されており、Goのツールチェーン(go vet, golangci-lintなど)の設定例や、避けるべきアンチパターンも含まれています。

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

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

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

ドキュメント

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

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

アクティベーション条件

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

中核となる原則

1. シンプルさと明確さ

Goは、巧妙さよりも単純さを優先します。コードは明白で読みやすくなければなりません。

// Good: 明確で直接的なコード
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
}

// Bad: 過度に巧妙
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. ゼロ値を有効活用する

初期化しなくてもすぐに使えるように型を設計します。

// Good: ゼロ値が有効

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

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

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

// Bad: 初期化が必要

type BadCounter struct {
    counts map[string]int // nilマップはパニックを引き起こす
}

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

関数はインターフェースパラメータを受け入れ、具象型を返すべきです。

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

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

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

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

// Good: コンテキスト付きでエラーをラップする
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)
}

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

// Bad: ブランク識別子でエラーを無視
result, _ := doSomething()

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

// Acceptable: エラーが本当に重要でない場合(稀)
_ = 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
}

ゴルーチンリークの回避

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

// Good: キャンセルを適切に処理
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
}

インターフェース設計

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

// Good: 単一メソッドのインターフェース
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

パッケージ命名

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

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

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

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

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

// Good: 依存性注入
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 を呼び出す

メモリとパフォーマンス

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

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

// Good: 単一割り当て
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()
}

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

// Bad: 多くの文字列割り当てを生成
func join(parts []string) string {
    var result string
    for _, p := range parts {
        result += p + ","
    }
    return result
}

// Good: 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()
}

// Best: 標準ライブラリを使用
func join(parts []string) string {
    return strings.Join(parts, ",")
}

Goツールチェイン統合

必須コマンド

# Build and run
go build ./...
go run ./cmd/myapp

# Testing
go test ./...
go test -race ./...
go test -cover ./...

# Static analysis
go vet ./...
staticcheck ./...
golangci-lint run

# Module management
go mod tidy
go mod verify

# Formatting
gofmt -w .
goimports -w .

推奨Linter設定 (.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 でフォーマットする
早期リターンエラーを最初に処理し、ハッピーパスはインデントなしに保つ

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

// Bad: 長い関数でのネイキッドリターン
func process() (result int, err error) {
    // ... 50 lines ...
    return // 何が返されているのか?
}

// Bad: 制御フローにパニックを使用
func GetUser(id string) *User {
    user, err := db.Find(id)
    if err != nil {
        panic(err) // こうしないでください
    }
    return user
}

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

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

// Bad: 値レシーバとポインタレシーバが混在
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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

関連スキル