Provides best practices for Go backend development, focusing on clean code, idiomatic error handling, and efficient concurrency.

Install

mkdir -p .claude/skills/golang && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9102" && unzip -o skill.zip -d .claude/skills/golang && rm skill.zip

Installs to .claude/skills/golang

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use when building Go backend services, implementing goroutines/channels, handling errors idiomatically, writing tests with testify, or following Go best practices for APIs/CLI tools.
182 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Implement idiomatic error handling
  • Manage goroutines and channels safely
  • Structure Go backend services
  • Write unit tests using testify
  • Apply Go naming and interface conventions

How it works

The skill applies community-validated patterns, such as small interface design and explicit error handling, to ensure code readability, maintainability, and concurrency safety.

Inputs & outputs

You give it
Go source code or architectural requirements
You get back
Idiomatic Go code or refactoring suggestions

When to use golang

  • Implementing idiomatic error handling
  • Optimizing goroutines and channels
  • Structuring Go APIs and services
  • Writing unit tests using testify

About this skill

Go Development Best Practices

Version: 2.0.0 Purpose: Comprehensive Go development patterns covering idioms, error handling, concurrency, testing, and quality Scope: Backend development with Go - API services, CLI tools, system software Prerequisites: Basic Go syntax knowledge

Overview

Go (Golang) is designed for simplicity, explicit error handling, and safe concurrent programming. This skill covers production-ready patterns validated by the Go community, official documentation, and industry standards (Uber Engineering, Google).

Core Philosophy:

  • Simplicity: "Clear is better than clever" - favor readable code over abstractions
  • Explicit over implicit: No exceptions, no hidden control flow, visible errors
  • Composition over inheritance: Interfaces and embedding, not class hierarchies
  • Built-in concurrency: Goroutines and channels as first-class primitives
  • Tooling-first: Format, vet, test, and benchmark built into the language

Key Design Principles:

  1. Small interfaces (1-3 methods ideal)
  2. Consumer-side interface placement
  3. Error values, not exceptions
  4. Happy path at left margin
  5. Goroutines must have explicit termination

1. Idiomatic Go Patterns

1.1 Naming Conventions

Package Names:

// ✅ GOOD: Package names are single lowercase identifiers
// Import path: "net/url" → package name: url
// Import path: "encoding/json" → package name: json
package url      // from "net/url"
package json     // from "encoding/json"
package strings

// ❌ BAD
package urls            // No plural
package encodingjson    // Don't smash words together
package stringutils     // Too verbose

Getters and Setters:

type Account struct {
    balance int
}

// ✅ GOOD: No "Get" prefix
func (a *Account) Balance() int {
    return a.balance
}

func (a *Account) SetBalance(amount int) {
    a.balance = amount
}

// ❌ BAD: Java-style getters
func (a *Account) GetBalance() int {
    return a.balance
}

Error Variables:

// Exported sentinel errors (capitalized)
var ErrNotFound = errors.New("not found")
var ErrTimeout = errors.New("timeout")

// Unexported internal errors (lowercase)
var errInternal = errors.New("internal error")

Interface Naming:

// ✅ GOOD: Short, descriptive
type Reader interface {
    Read(p []byte) (n int, err error)
}

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

// ❌ BAD: Verbose or unclear
type DataReader interface { ... }
type IReader interface { ... }  // No "I" prefix

1.2 Interface Design - "The Bigger the Interface, the Weaker the Abstraction"

Core Principle: Small, consumer-side interfaces provide maximum flexibility.

Single-Method Interfaces (Ideal):

// Standard library examples
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
}

// Compose interfaces
type ReadCloser interface {
    Reader
    Closer
}

Consumer-Side Interface Placement:

// ❌ WRONG: Producer defines interface
package store

type CustomerStorage interface {
    StoreCustomer(Customer) error
    GetCustomer(string) (Customer, error)
    UpdateCustomer(Customer) error
    // 10+ methods...
}

type PostgresStore struct {}
func (s *PostgresStore) StoreCustomer(...) { ... }

// ✅ CORRECT: Consumer defines what it needs
package client

type customerGetter interface {
    GetCustomer(string) (store.Customer, error)
}

func ProcessCustomer(cg customerGetter) {
    customer, _ := cg.GetCustomer("123")
    // Only depends on GetCustomer method
}

Return Concrete Types, Accept Interfaces (Postel's Law):

// ✅ GOOD
func NewStore() *PostgresStore {
    return &PostgresStore{}
}

func Process(storage CustomerStorage) error {
    // Accepts interface
}

// ❌ BAD: Returning interface
func NewStore() CustomerStorage {
    return &PostgresStore{}
}

When to Create Interfaces:

  • Multiple implementations exist or are planned
  • Need for testing (mocking dependencies)
  • Decoupling packages
  • NOT for: Single implementation with no testing need

1.3 Happy Path Left, Early Returns

Core Principle: Align success path to left margin, handle errors first.

// ❌ BAD: Deep nesting
func join(s1, s2 string, max int) (string, error) {
    if s1 == "" {
        return "", errors.New("s1 is empty")
    } else {
        if s2 == "" {
            return "", errors.New("s2 is empty")
        } else {
            concat, err := concatenate(s1, s2)
            if err != nil {
                return "", err
            } else {
                if len(concat) > max {
                    return concat[:max], nil
                } else {
                    return concat, nil
                }
            }
        }
    }
}

// ✅ GOOD: Happy path aligned left
func join(s1, s2 string, max int) (string, error) {
    if s1 == "" {
        return "", errors.New("s1 is empty")
    }
    if s2 == "" {
        return "", errors.New("s2 is empty")
    }

    concat, err := concatenate(s1, s2)
    if err != nil {
        return "", err
    }

    if len(concat) > max {
        return concat[:max], nil
    }
    return concat, nil
}

Guidelines:

  • Maximum 3-4 levels of nesting
  • Omit else blocks when if returns
  • Handle errors immediately
  • Keep normal flow at lowest indentation

1.4 Composition Over Inheritance

Type Embedding (Struct Composition):

// Embedding for method promotion
type Logger struct {
    *log.Logger
    prefix string
}

func NewLogger(prefix string) *Logger {
    return &Logger{
        Logger: log.New(os.Stdout, "", 0),
        prefix: prefix,
    }
}

// Logger methods automatically available
logger := NewLogger("APP")
logger.Println("message") // Calls embedded log.Logger.Println

Interface Composition:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// Compose interfaces
type ReadCloser interface {
    Reader
    Closer
}

Warning: Avoid embedding in public APIs:

// ❌ BAD: Exposes implementation details
type MyHandler struct {
    http.Handler // Leaks all Handler methods
}

// ✅ GOOD: Explicit delegation
type MyHandler struct {
    handler http.Handler
}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Custom logic
    h.handler.ServeHTTP(w, r)
}

1.5 Key Go Idioms

Defer for Cleanup:

func processFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close() // Guaranteed cleanup

    // Multiple returns, all close file
    if condition {
        return nil // File closed
    }

    return process(f) // File closed
}

// Mutex pattern
func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()

    c.value++ // All paths unlock
}

Critical Rule: Call defer AFTER checking error:

// ❌ WRONG
defer f.Close() // f is nil if Open failed
f, err := os.Open(path)

// ✅ CORRECT
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()

Multiple Return Values:

// (value, error) - Standard error handling
func GetUser(id string) (*User, error) {
    // ...
}

// (value, bool) - "comma ok" idiom
value, ok := myMap[key]
if !ok {
    // key not found
}

result, ok := someValue.(TargetType)
if !ok {
    // type assertion failed
}

data, ok := <-channel
if !ok {
    // channel closed
}

Blank Identifier _:

// Ignore unwanted values
_, err := os.Open(filename)

// Compile-time interface check
var _ http.Handler = (*MyHandler)(nil)

// Import for side effects
import _ "net/http/pprof"

Useful Zero Values:

// sync.Mutex - ready to use
var mu sync.Mutex
mu.Lock() // Works immediately

// bytes.Buffer - valid empty buffer
var buf bytes.Buffer
buf.WriteString("hello") // No initialization needed

// Slices - safe to read
var s []int
fmt.Println(len(s)) // 0 (safe)

2. Error Handling

2.1 Error Wrapping with %w (Go 1.13+)

Core Pattern: Wrap errors with context using fmt.Errorf and %w.

func processFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        // Wrap with context using %w
        return fmt.Errorf("failed to open file %s: %w", path, err)
    }
    defer file.Close()

    data, err := io.ReadAll(file)
    if err != nil {
        return fmt.Errorf("failed to read file %s: %w", path, err)
    }

    return processData(data)
}

// Result when error bubbles up:
// "failed to initialize: failed to open file config.json: open config.json: no such file or directory"

Checking Wrapped Errors:

// errors.Is - Check for specific error in chain
if errors.Is(err, os.ErrNotExist) {
    fmt.Println("File doesn't exist")
}

// errors.As - Extract specific error type
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    fmt.Printf("Path error on: %s\n", pathErr.Path)
}

Critical: Use %w, NOT %v:

// ❌ WRONG: Breaks error chain
return fmt.Errorf("failed: %v", err)

// ✅ CORRECT: Preserves chain
return fmt.Errorf("failed: %w", err)

2.2 Sentinel Errors vs Custom Error Types

Sentinel Errors (Package-Level Variables):

package db

var (
    ErrConnectionFailed = errors.New("database connection failed")
    ErrRecordNotFound   = errors.New("record not found")
    ErrDuplicateKey     = errors.New("duplicate key violation")
)

func GetUser(id int) (*User, error) {
    // ...
    if notFound {
        return nil, ErrRecordNotFound
    }
    return user, nil
}

// Caller checks with errors.Is
user, err := db.GetUser(123)
if errors.Is(err, db.ErrRecordNotFound) {
    // Handle not found
}

Custom Error Types (Rich Context):

type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

fun

---

*Content truncated.*

When not to use it

  • When building high-level UI applications requiring complex class hierarchies
  • When performance requirements necessitate manual memory management

Prerequisites

Basic Go syntax knowledge

Limitations

  • Requires adherence to Go-specific design philosophy
  • Does not support legacy object-oriented design patterns

How it compares

It enforces Go-specific idioms like composition over inheritance and consumer-side interface definition, contrasting with traditional object-oriented patterns.

Compared to similar skills

golang side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
golang (this skill)36moReviewIntermediate
rr05moNo flagsAdvanced
effective-go3239moNo flagsBeginner
architecture-patterns552moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by MadAppGang

View all by MadAppGang

claudish-usage

MadAppGang

CRITICAL - Guide for using Claudish CLI ONLY through sub-agents to run Claude Code with any AI model (OpenRouter, Gemini, OpenAI, local models). NEVER run Claudish directly in main context unless user explicitly requests it. Use when user mentions external AI models, Claudish, OpenRouter, Gemini, OpenAI, Ollama, or alternative models. Includes mandatory sub-agent delegation patterns, agent selection guide, file-based instructions, and strict rules to prevent context window pollution.

442

golang-performance

MadAppGang

Use when profiling Go applications (pprof), running benchmarks, optimizing memory/CPU usage, or debugging performance bottlenecks in production Go code.

47

schemas

MadAppGang

YAML frontmatter schemas for Claude Code agents and commands. Use when creating or validating agent/command files.

34

external-model-selection

MadAppGang

Choose optimal external AI models for code analysis, bug investigation, and architectural decisions. Use when consulting multiple LLMs via claudish, comparing model perspectives, or investigating complex Go/LSP/transpiler issues. Provides empirically validated model rankings (91/100 for MiniMax M2, 83/100 for Grok Code Fast) and proven consultation strategies based on real-world testing.

218

adr-documentation

MadAppGang

Architecture Decision Records (ADR) documentation practice. Use when documenting architectural decisions, recording technical trade-offs, creating decision logs, or establishing architectural patterns. Trigger keywords - "ADR", "architecture decision", "decision record", "trade-offs", "architectural decision", "decision log".

12

claudemem-orchestration

MadAppGang

Use when orchestrating multi-agent code analysis with claudemem. Run claudemem once, share output across parallel agents. Enables parallel investigation, consensus analysis, and role-based command mapping.

12

You might also like

rr

west2-online

当用户调用此 skill 时,请按以下步骤执行:

00

effective-go

openshift

Apply Go best practices, idioms, and conventions from golang.org/doc/effective_go. Use when writing, reviewing, or refactoring Go code to ensure idiomatic, clean, and efficient implementations.

323536

architecture-patterns

wshobson

Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.

55214

workflow-orchestration-patterns

wshobson

Design durable workflows with Temporal for distributed systems. Covers workflow vs activity separation, saga patterns, state management, and determinism constraints. Use when building long-running processes, distributed transactions, or microservice orchestration.

10117

go-dev-guidelines

jumppad-labs

This skill should be used when writing, refactoring, or testing Go code. It provides idiomatic Go development patterns, TDD-based workflows, project structure conventions, and testing best practices using testify/require and mockery. Activate this skill when creating new Go features, services, packages, tests, or when setting up new Go projects.

1495

golang-pro

sickn33

Master Go 1.21+ with modern patterns, advanced concurrency, performance optimization, and production-ready microservices. Expert in the latest Go ecosystem including generics, workspaces, and cutting-edge frameworks. Use PROACTIVELY for Go development, architecture design, or performance optimization.

1479

Search skills

Search the agent skills registry