Skip to content
Back to skills

Go

ASecurity

Idiomatic programming style, patterns, and conventions for Go (Golang) development. Trigger when: - Writing, refactoring, reviewing, or debugging Go code. - Files matching the pattern **/*.go (including go.mod, go.sum) are in the workspace or referenced. - Tasks involve: go fmt, go test, go build, go vet, golangci-lint. - Prompt contains keywords: go, golang, goroutine, channel, select, interface, err != nil, defer, panic, gofmt, struct.

  • 10 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 1, 2026
code-qualitygobashdebuggingrefactoringdocumentation

Works with

  • cli

Security analysis

A100/100

Scanned September 1, 2026

npx -y skills add nrdxp/predicate --skill go --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Go?

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

Security grade badge for Go
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nrdxp-predicate/badge)](https://www.skillsdirectory.com/skills/nrdxp-predicate)

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
description: |
  Idiomatic programming style, patterns, and conventions for Go (Golang) development.
  Trigger when:
  - Writing, refactoring, reviewing, or debugging Go code.
  - Files matching the pattern **/*.go (including go.mod, go.sum) are in the workspace or referenced.
  - Tasks involve: go fmt, go test, go build, go vet, golangci-lint.
  - Prompt contains keywords: go, golang, goroutine, channel, select, interface, err != nil, defer, panic, gofmt, struct.
---

# Go Language Idioms

Go favors simplicity, readability, and explicit behavior. Work with the language's conventions, not against them.

---

## Core Philosophy

- **Simplicity over cleverness** — clear, boring code is better than clever, obscure code
- **Explicit over implicit** — errors are values, not exceptions
- **Composition over inheritance** — embed types, implement interfaces
- **Share by communicating** — don't communicate by sharing memory

---

## Formatting

Let `gofmt` handle it. Don't fight the formatter.

```bash
gofmt -w .        # Format all files
go fmt ./...      # Format package
```

All Go code in the ecosystem follows `gofmt`. There is no debate.

---

## Naming

### General Rules

| Scope      | Style                  | Example                                       |
| :--------- | :--------------------- | :-------------------------------------------- |
| Exported   | `PascalCase`           | `ReadFile`, `HTTPClient`                      |
| Unexported | `camelCase`            | `readFile`, `httpClient`                      |
| Packages   | lowercase, single word | `bytes`, `http`, `bufio`                      |
| Acronyms   | All caps               | `HTTP`, `URL`, `ID` (not `Http`, `Url`, `Id`) |

### Package Names

- Short, concise, evocative
- No underscores or mixedCaps
- The package name is part of the type name: `bufio.Reader`, not `bufio.BufReader`

### Getters and Setters

```go
// ❌ Don't prefix getters with "Get"
func (u *User) GetName() string

// ✅ Just use the field name
func (u *User) Name() string

// ✅ Setters use "Set" prefix
func (u *User) SetName(name string)
```

### Interface Names

One-method interfaces use method name + `-er` suffix:

| Method   | Interface  |
| :------- | :--------- |
| `Read`   | `Reader`   |
| `Write`  | `Writer`   |
| `Close`  | `Closer`   |
| `String` | `Stringer` |

---

## Functions

### Multiple Return Values

Go functions return `(result, error)` instead of throwing:

```go
func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    return io.ReadAll(f)
}
```

### Named Return Values

Use for documentation, but avoid naked returns in non-trivial functions:

```go
// Named returns document what's being returned
func ParseDuration(s string) (duration time.Duration, err error) {
    // ...
    return duration, nil  // Explicit, not naked
}
```

### Defer

Use `defer` for cleanup. Defers execute LIFO at function exit:

```go
func CopyFile(dst, src string) error {
    r, err := os.Open(src)
    if err != nil {
        return err
    }
    defer r.Close()

    w, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer w.Close()

    _, err = io.Copy(w, r)
    return err
}
```

---

## Interfaces

### Keep Interfaces Small

Accept interfaces, return concrete types:

```go
// ✅ Function accepts interface (flexible)
func Process(r io.Reader) error

// ✅ Function returns concrete type (useful)
func NewClient() *Client
```

### Interface Satisfaction

Interfaces are satisfied implicitly—no `implements` keyword:

```go
type Stringer interface {
    String() string
}

type User struct{ Name string }

// User satisfies Stringer automatically
func (u User) String() string {
    return u.Name
}
```

### Compile-Time Check

Verify interface satisfaction at compile time:

```go
var _ io.Reader = (*MyReader)(nil)
```

---

## Pointers vs. Values

### When to Use Pointer Receivers

| Use Pointer | When                                                 |
| :---------- | :--------------------------------------------------- |
| `*T`        | Method modifies the receiver                         |
| `*T`        | Struct is large (avoid copying)                      |
| `*T`        | Consistency (if any method uses pointer, all should) |

### When to Use Value Receivers

| Use Value | When                              |
| :-------- | :-------------------------------- |
| `T`       | Method doesn't modify receiver    |
| `T`       | Type is small (int, small struct) |
| `T`       | Type is immutable by design       |

---

## Concurrency

### Goroutines

Lightweight, start with `go`:

```go
go func() {
    // runs concurrently
}()
```

### Channels

Use channels for communication between goroutines:

```go
ch := make(chan int)       // Unbuffered
ch := make(chan int, 100)  // Buffered

ch <- value   // Send
v := <-ch     // Receive
```

### Share by Communicating

> "Don't communicate by sharing memory; share memory by communicating."

```go
// ❌ Shared state with mutex
var count int
var mu sync.Mutex

// ✅ Channel-based coordination
type result struct{ value int }
results := make(chan result)
```

### Common Patterns

| Pattern           | Use Case                          |
| :---------------- | :-------------------------------- |
| `sync.WaitGroup`  | Wait for N goroutines to complete |
| `context.Context` | Cancellation and timeouts         |
| `select`          | Multiplex channel operations      |
| `sync.Once`       | One-time initialization           |

---

## Error Handling

### Errors Are Values

Check explicitly. Never discard with `_`:

```go
// ❌ Never ignore errors
result, _ := doSomething()

// ✅ Always check
result, err := doSomething()
if err != nil {
    return fmt.Errorf("doing something: %w", err)
}
```

### Error Wrapping

Use `%w` to wrap errors with context:

```go
if err != nil {
    return fmt.Errorf("failed to parse config %s: %w", path, err)
}
```

### Custom Errors

For libraries, define sentinel errors or custom types:

```go
// Sentinel errors
var ErrNotFound = errors.New("not found")

// Custom error types
type ValidationError struct {
    Field   string
    Message string
}

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

### When to Panic

| Context           | Panic?        | Alternative       |
| :---------------- | :------------ | :---------------- |
| Library code      | ❌ Never      | Return `error`    |
| Impossible states | ✅ Acceptable | Assert invariants |
| Init failures     | ✅ Acceptable | `log.Fatal`       |

---

## Anti-Patterns

| Anti-Pattern            | Description                                  | Remedy                                             |
| :---------------------- | :------------------------------------------- | :------------------------------------------------- |
| **Ignoring errors**     | Using `_` to discard errors                  | Always check or explicitly document why ignored    |
| **Naked returns**       | `return` without values in complex functions | Use explicit return values                         |
| **Interface pollution** | Defining interfaces before needed            | Define interfaces at point of use                  |
| **Premature channels**  | Using channels when mutex suffices           | Start simple; add concurrency primitives as needed |
| **Giant interfaces**    | Interfaces with many methods                 | Keep interfaces small; compose when needed         |

---

## Tooling

```bash
go fmt ./...           # Format code
go vet ./...           # Static analysis
golangci-lint run      # Comprehensive linting
go test ./...          # Run tests
go test -race ./...    # Detect race conditions
```

---

## Quick Reference

- **Errors are values** — check explicitly, wrap with context
- **Accept interfaces, return structs** — flexible inputs, concrete outputs
- **Use `gofmt`** — no formatting debates
- **Prefer composition** — embed types, small interfaces
- **Channels for coordination** — share by communicating
- **`defer` for cleanup** — runs at function exit, LIFO
- **No naked returns** — be explicit in non-trivial functions

> These idioms refine but are subordinate to the [Code-Edit Constraints](../../ambient.md#code-edit-constraints).

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.