について
このスキルは、Goにおける適切なコンテキストの使用方法について、伝播、キャンセル、タイムアウト、デッドライン、値の取り扱いを網羅し、一般的なアンチパターンを強調しながら解説します。コンテキスト実装に関する質問にご利用いただけますが、より広範な並行処理パターン、HTTPミドルウェア、またはエラーハンドリングのシナリオには対応していません。
クイックインストール
Claude Code
推奨npx skills add eduardo-sl/go-agent-skills -a claude-code/plugin add https://github.com/eduardo-sl/go-agent-skillsgit clone https://github.com/eduardo-sl/go-agent-skills.git ~/.claude/skills/go-contextこのコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします
ドキュメント
Go Context
context.Context controls cancellation, deadlines, and request-scoped values
across API boundaries. Misusing it causes goroutine leaks, orphaned work,
and subtle production bugs.
1. Core Rules
Context is always the first parameter:
// ✅ Good — context is first
func GetUser(ctx context.Context, id string) (*User, error)
func (s *Service) Process(ctx context.Context, req Request) error
// ❌ Bad — context buried in the middle or end
func GetUser(id string, ctx context.Context) (*User, error)
func Process(req Request, ctx context.Context) error
NEVER store context in a struct:
// ❌ Bad — context stored in struct
type Server struct {
ctx context.Context // NEVER do this
cancel context.CancelFunc
}
// ✅ Good — pass context through method parameters
func (s *Server) Shutdown(ctx context.Context) error {
return s.httpServer.Shutdown(ctx)
}
Context represents the lifetime of a single operation, not the lifetime of an object.
NEVER pass nil context:
// ❌ Bad
doSomething(nil, data)
// ✅ Good — use context.TODO() if unsure which context to use
doSomething(context.TODO(), data)
// ✅ Good — use context.Background() for top-level/main
doSomething(context.Background(), data)
2. Cancellation
Always defer cancel:
// ✅ Good — cancel called even if operation succeeds
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
result, err := longOperation(ctx)
Failing to call cancel leaks resources (timers, goroutines) until the parent context is cancelled.
Use WithCancel for manual cancellation:
func (s *Supervisor) Run(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return s.runWorkerA(ctx) })
g.Go(func() error { return s.runWorkerB(ctx) })
// If any worker returns an error, errgroup cancels ctx,
// which signals all other workers to stop.
return g.Wait()
}
Check context cancellation in loops:
// ✅ Good — respects cancellation
func processItems(ctx context.Context, items []Item) error {
for _, item := range items {
if err := ctx.Err(); err != nil {
return fmt.Errorf("processing cancelled: %w", err)
}
if err := process(ctx, item); err != nil {
return fmt.Errorf("process item %s: %w", item.ID, err)
}
}
return nil
}
// ❌ Bad — runs to completion even if cancelled
func processItems(ctx context.Context, items []Item) error {
for _, item := range items {
process(ctx, item) // ignores ctx cancellation between items
}
return nil
}
3. Timeouts and Deadlines
WithTimeout for duration-based limits:
func (c *Client) FetchUser(ctx context.Context, id string) (*User, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url+"/users/"+id, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("fetch user %s: %w", id, err)
}
defer resp.Body.Close()
// ...
}
WithDeadline for absolute time limits:
// Use when coordinating with external deadlines (SLAs, cron windows)
deadline := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
ctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
Timeout budgets — don't exceed parent timeout:
// ✅ Good — child timeout shorter than parent
func handler(ctx context.Context) error {
// Parent has 30s timeout (from HTTP server)
// Give DB query 5s of the 30s budget
dbCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
data, err := db.QueryContext(dbCtx, query)
// Give external API 10s of the remaining budget
apiCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
result, err := client.Call(apiCtx, data)
return nil
}
// ❌ Bad — child timeout exceeds parent (silently capped anyway)
ctx, cancel := context.WithTimeout(parentCtx, 60*time.Second) // parent has 5s left
// This timeout is 60s but will actually fire at parent's deadline
Check if deadline exists:
if deadline, ok := ctx.Deadline(); ok {
remaining := time.Until(deadline)
if remaining < minRequired {
return fmt.Errorf("insufficient time remaining: %v", remaining)
}
}
4. Context Values
Use sparingly — only for request-scoped metadata:
// ✅ Appropriate uses:
// - Request ID
// - Trace/span ID
// - Authenticated user info
// - Request-scoped logger
// ❌ Bad uses:
// - Database connections (use dependency injection)
// - Configuration (use struct fields)
// - Function parameters (pass explicitly)
Use unexported key types to prevent collisions:
// ✅ Good — unexported type prevents key collisions
type contextKey struct{}
var requestIDKey = contextKey{}
func WithRequestID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, requestIDKey, id)
}
func RequestID(ctx context.Context) string {
id, _ := ctx.Value(requestIDKey).(string)
return id
}
// ❌ Bad — string keys risk collisions across packages
ctx = context.WithValue(ctx, "request_id", id) // any package could overwrite this
Always provide accessor functions — never expose the key:
// ✅ Good — clean API with accessors
rid := middleware.RequestID(ctx)
// ❌ Bad — exposes internal key type
rid := ctx.Value(requestIDKey).(string) // caller needs key, risks panic on nil
5. Context in HTTP Handlers
Use r.Context() for the request context:
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // carries cancellation when client disconnects
user, err := h.service.GetUser(ctx, id)
if err != nil {
if errors.Is(err, context.Canceled) {
return // client disconnected, no point writing response
}
// handle error...
}
// ...
}
Attach values via middleware:
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, err := authenticate(r)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
ctx := WithUser(r.Context(), user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
6. Context in Testing
Use context with timeout in tests to prevent hangs:
func TestSlowOperation(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := slowOperation(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// assert result...
}
Test cancellation behavior:
func TestCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately
_, err := operation(ctx)
if !errors.Is(err, context.Canceled) {
t.Errorf("expected context.Canceled, got %v", err)
}
}
7. context.Background() vs context.TODO()
| Function | When to use |
|---|---|
context.Background() | Top-level: main(), init(), test setup. Intentional root context. |
context.TODO() | Placeholder when you don't know which context to use yet. Signals "this needs to be fixed". |
context.TODO() is a code smell in production code — replace it before shipping.
Verification Checklist
context.Contextis the first parameter in all functions that accept it- No context stored in struct fields
defer cancel()called immediately afterWithCancel,WithTimeout,WithDeadline- Long loops check
ctx.Err()between iterations - Child timeouts don't exceed parent timeout budget
- Context values use unexported key types with accessor functions
- Only request-scoped metadata stored in context values (not configs, connections)
- HTTP handlers use
r.Context()and pass it downstream - No
nilcontext passed — usecontext.TODO()orcontext.Background() - Tests use
context.WithTimeoutto prevent hanging
GitHub リポジトリ
よくある質問
go-context Skillとは何ですか?
go-context はeduardo-sl が作成した Claude Skillです。Skillは、Claudeが必要に応じて読み込む指示とリソースをまとめ、追加の指示なしで go-context に関連するタスクを実行できるようにします。
go-context をインストールするには?
このページのインストールコマンドを使用してください。go-context をプラグインとして Claude Code に追加するか、リポジトリを skills ディレクトリにクローンし、Claudeを再起動してSkillを読み込みます。
go-context はどのカテゴリに属しますか?
go-context は デザイン カテゴリに属します。
go-context は無料で利用できますか?
はい。go-context は AIMCP に掲載されており、無料でインストールできます。
関連スキル
executing-plansスキルは、完全な実装計画があり、それを管理されたバッチでレビューチェックポイントを設けながら実行する場合に使用します。このスキルは計画を読み込んで批判的にレビューした後、小さなバッチ(デフォルトは3タスク)でタスクを実行し、各バッチの間に進捗状況を報告してアーキテクトのレビューを受けます。これにより、品質管理チェックポイントが組み込まれた体系的な実装が保証されます。
このスキルは、コードレビュアーサブエージェントを起動し、処理を進める前に要件に対してコード変更を分析します。タスク完了後、主要な機能の実装後、またはmainブランチへのマージ前などに使用すべきです。このレビューは、現在の実装と元の計画を比較することで、問題を早期に発見するのに役立ちます。
このスキルは、開発者がHTTP、stdio、またはSSEトランスポートを使用してMCPサーバーをClaude Codeに接続するための包括的なガイドを提供します。GitHub、Notion、カスタムAPIなどの外部サービスを統合するためのインストール、設定、認証、セキュリティについて解説しています。MCP統合のセットアップ、外部ツールの設定、またはClaudeのModel Context Protocolを扱う際にご利用ください。
このスキルは、タスク分析に基づいて開発者がClaude Code WebとCLIインターフェースの選択を支援し、これらの環境間でのシームレスなセッションテレポーテーションを可能にします。Web、CLI、モバイル環境を切り替える際のセッション状態とコンテキストを管理することで、ワークフローを最適化します。様々な段階で異なるツールを必要とする複雑なプロジェクトにご活用ください。
