Skip to content
Agentmart
Submit a tool

go-defensive-coding

Prevent panics, silent corruption, and subtle runtime bugs in Go: typed-nil interfaces, slice aliasing, integer overflow on conversion, float comparison…

Skill by eduardo-sl

IndexedMIT★ 82
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/eduardo-sl-go-defensive-coding

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Prevent panics, silent corruption, and subtle runtime bugs in Go: typed-nil interfaces, slice aliasing, integer overflow on conversion, float comparison, defer in loops, defensive copying at API boundaries, and zero-value design. Use when hardening code against crashes, reviewing for nil-safety, converting between numeric types, or deciding what to copy at a package boundary. Trigger examples: "nil pointer panic", "why is my error not nil", "slice aliasing", "integer overflow", "compare floats", "defer in a loop", "defensive copy", "make this crash-proof". Not for: data races and goroutine lifecycle (go-concurrency-review), injection and auth (go-security-audit), a panic that already happened (go-troubleshooting).

Use cases: design, backend, data, security

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/go-defensive-coding

Tools it pre-approves 9

From allowed-tools in SKILL.md: the agent may use these without asking you each time.

  • Read
  • Edit
  • Write
  • Glob
  • Grep
  • Bash(go:*)
  • Bash(gofmt:*)
  • Bash(golangci-lint:*)
  • Bash(gosec:*)

What's inside

Skill name
go-defensive-coding
Description
Prevent panics, silent corruption, and subtle runtime bugs in Go: typed-nil interfaces, slice aliasing, integer overflow on conversion, float comparison, defer in loops, defensive copying at API boundaries, and zero-value design. Use when hardening code against crashes, reviewing for nil-safety, converting between numeric types, or deciding what to copy at a package boundary. Trigger examples: "nil pointer panic", "why is my error not nil", "slice aliasing", "integer overflow", "compare floats", "defer in a loop", "defensive copy", "make this crash-proof". Not for: data races and goroutine lifecycle (go-concurrency-review), injection and auth (go-security-audit), a panic that already happened (go-troubleshooting).
License
MIT
Compatibility
Designed for Claude Code or similar AI coding agents working on Go projects. Requires the Go toolchain. golangci-lint and gosec are optional, for enforcing the rules below.
Allowed tools
Read, Edit, Write, Glob, Grep, Bash(go:*), Bash(gofmt:*), Bash(golangci-lint:*), Bash(gosec:*)
Metadata
author: eduardo-sl · version: 1.1.3
Instructions
229 lines in SKILL.md · view on GitHub

Before installing

  • Pre-approves some shell commands

Files (4)

  • SKILL.md
  • copies-and-lifecycle.md · references/
  • nil-and-aliasing.md · references/
  • numeric-safety.md · references/

SKILL.md (first lines)

# Go Defensive Coding

Go has no exceptions and no null-safety in the type system. Every trap below
compiles cleanly, passes review, and fails in production.

Detailed reference material, loaded on demand:

- `references/nil-and-aliasing.md` — the full typed-nil rules, slice
  aliasing scenarios, and memory retention.
- `references/numeric-safety.md` — conversion range checks, overflow
  detection, float and time comparison.
- `references/copies-and-lifecycle.md` — `copylocks`/`noCopy`, `defer` in
  loops, checking deferred `Close` errors, usable zero values.

Read a reference file only when the section below is not enough.

## Operating Modes

- **Harden** — you are writing or changing code. Apply every rule as you go.
- **Review** — you are auditing existing code. Report findings with severity
  (🔴 panic or corruption, 🟡 latent bug, 🟢 style) and cite file:line.

## 1. The Typed-Nil Interface Trap

A non-nil interface can hold a nil pointer. This is the single most common
source of "impossible" nil checks in Go.

```go
type NotFoundError struct{ ID string }

func (e *NotFoundError) Error() string { return "not found: " + e.ID }

// ❌ Bad — returns a non-nil error even on success
func find(id string) error {
    var err *NotFoundError // typed nil
    if id == "" {
        err = &NotFoundError{ID: id}
    }
    return err // interface is (type=*NotFoundError, value=nil) — NOT nil
}

// ✅ Good — return the untyped nil literal
func find(id string) error {
    if id == "" {
        return &NotFoundError{ID: id}
    }
    return nil
}
```

Rules:

- Never declare a concrete error/pointer variable and return it as an
  interface. Return `nil` explicitly on the success path.
- Never store a possibly-nil concrete pointer in an `error`, `io.Reader`, or
  any interface-typed struct field.
- `staticcheck` reports `SA4023` when a comparison with `nil` can never be
  true because of this trap. No tool catches every case.

## 2. Nil Map, Slice, and Channel Behaviour

Memorise this table — half of these are safe and half panic or hang.

| Operation | nil map | nil slice | nil channel |
|---|---|---|---|
| Read / receive | zero value | index panics | blocks forever |
| Write / send | **panics** | `append` works | blocks forever |
| `len` / `cap` | `0` | `0` | `0` |
| `range` | zero iterations | zero iterations | blocks forever |
| `close` | n/a | n/a | **panics** |

```go
// ✅ A nil slice is a valid empty slice — do not guard append
var out []string
out = append(out, "a")

// ❌ A nil map is read-only
var m map[string]int
m["k"] = 1 // panic: assignment to entry in nil map

// ✅ Initialise every map before writing
m := make(map[string]int)
```

Return nil slices, not `[]T{}`. They marshal identically in JSON for
`encoding/json` when the field is `omitempty`, and cost no allocation.
Return an empty non-nil map only when the caller is documented to write to it.

## 3. Slice Aliasing

A slice is a view. `append` writes through that view whenever capacity
allows, mutating data the caller still owns.

```go
a := []int{1, 2, 3, 4}
b := a[:2]
b = append(b, 99) // ❌ overwrites a[2]; a is now [1 2 99 4]
```

```go
// ✅ Full slice expression caps the view — append must reallocate
b := a[:2:2]
b = append(b, 99) // a is untouched
```

Apply this whenever you hand a subslice to code you do not control, and
whenever a struct field holds a subslice of a larger buffer.

A subslice also keeps the entire backing array alive. To release a large
buffer, copy what you need: `head := slices.Clone(buf[:64])`.

## 4. Defensive Copying at Boundaries

Slices and maps are reference types. Storing or returning one without a copy
hands out a mutable handle to your internals.

```go
type Config struct{ hosts []string }

From eduardo-sl/go-agent-skills, MIT.

More from eduardo-sl/go-agent-skills

Signals

Stars
82
Forks
10
Open issues
0
Last commit (this folder)
Oct 14, 2026 (15 days ago)

From GitHub (fetched Oct 24, 2026).

Details

Version
1.1.3
License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from eduardo-sl

Cite this page

go-defensive-coding skill. Agent Mart. https://agentmart.dev/skills/eduardo-sl-go-defensive-coding (last updated 2026-10-24)