Skip to content
Back to skills

Go Best Practices

ASecurity

Use when reading or writing Go files (.go, go.mod).

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 16, 2026
ai-agentsgotesting

Works with

  • cli

Security analysis

A100/100

Scanned October 16, 2026

npx -y skills add alleneubank/agent-profile --skill go-best-practices --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Go Best Practices?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Go Best Practices
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/alleneubank-go-best-practices/badge)](https://www.skillsdirectory.com/skills/alleneubank-go-best-practices)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: go-best-practices
description: Use when reading or writing Go files (.go, go.mod).
---

# Go Best Practices

Follows the type, error, and assertion law in `code-law`. This skill covers language-specific idioms only.

## Make Illegal States Unrepresentable

Use Go's type system to prevent invalid states at compile time.

**Custom types for domain primitives:**
```go
// Distinct types prevent mixing up IDs
type UserID string
type OrderID string

func GetUser(id UserID) (*User, error) {
    // Compiler prevents passing OrderID here
}

// Methods attach behavior to the type
func (id UserID) String() string {
    return string(id)
}
```

**Interfaces for behavior contracts:**
```go
// Define what you need, not what you have
type UserRepository interface {
    GetByID(ctx context.Context, id UserID) (*User, error)
    Save(ctx context.Context, user *User) error
}

// Accept interfaces, return structs
func ProcessInput(r io.Reader) ([]byte, error) {
    return io.ReadAll(r)
}
```

**Enums with iota and exhaustive switch:**
```go
type Status int

const (
    StatusActive Status = iota + 1
    StatusInactive
    StatusPending
)

func ProcessStatus(s Status) (string, error) {
    switch s {
    case StatusActive:
        return "processing", nil
    case StatusInactive:
        return "skipped", nil
    case StatusPending:
        return "waiting", nil
    default:
        return "", fmt.Errorf("unhandled status: %v", s)
    }
}
```

**Functional options for flexible construction:**
```go
type ServerOption func(*Server)

func WithPort(port int) ServerOption {
    return func(s *Server) { s.port = port }
}

func NewServer(opts ...ServerOption) *Server {
    s := &Server{port: 8080, timeout: 30 * time.Second}
    for _, opt := range opts {
        opt(s)
    }
    return s
}
// Usage: NewServer(WithPort(3000), WithTimeout(time.Minute))
```

**Embed for composition:**
```go
type Timestamps struct {
    CreatedAt time.Time
    UpdatedAt time.Time
}

type User struct {
    Timestamps  // User gains CreatedAt, UpdatedAt
    ID    UserID
    Email string
}
```

**Restrict who can do it** — when only one scope may perform an effect, unexport its functions and export only the scope that performs it; put packages that other modules must not import under `internal/`. A capability with an unexported field can be created only by its own package. Correct types are the proof.

## Go-Specific Error Handling

Wrap errors with `%w` to preserve the chain for `errors.Is` / `errors.As`:
```go
out, err := client.Do(ctx, req)
if err != nil {
    return nil, fmt.Errorf("fetch widget failed: %w", err)
}
```

## Structured Logging

Use `log/slog` with structured key-value pairs:
```go
import "log/slog"

var log = slog.With("component", "widgets")

func createWidget(name string) (*Widget, error) {
    log.Debug("creating widget", "name", name)
    widget := &Widget{Name: name}
    log.Debug("created widget", "id", widget.ID)
    return widget, nil
}
```

## Test Doubles

`gomock.InOrder(...)` and `mock.EXPECT().Fetch(id).Times(n)` (`go.uber.org/mock`) standing in for your own collaborator assert which calls happened, not what the code produced — the change-detector shape in `testing-best-practices`. Accept the interface, pass a hand-written fake, and assert the return value in a table test; keep the call assertion only where the count *is* the contract, such as a retry budget.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.