Skip to content
Development
Skill

/golang-safety

Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use when encountering nil panics, append aliasing, map concurrent access, float comparison pitfalls, or zero-value design questions. Also use when reviewing code for nil-safety, numeric

From plugin
cc-skills-golang
2.9k46 skills
Install
$ npx -y skills add samber/cc-skills-golang --skill golang-safety --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition โ†’
  • You can call itInvoke it directly when you want it.
  • Slash command/golang-safety

Context preview

The summary Claude sees to decide when to auto-load this skill.

Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use when encountering nil panics, append aliasing, map concurrent access, float comparison pitfalls, or zero-value design questions. Also use when reviewing code for nil-safety, numeric

SKILL.md

golang-safety.SKILL.md
name: golang-safety
description: "Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use when encountering nil panics, append aliasing, map concurrent access, float comparison pitfalls, or zero-value design questions. Also use when reviewing code for nil-safety, numeric conversion overflow, resource lifecycle issues (defer in loops), or defensive copying of slices and maps."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code or similar AI coding agents, and for projects using Golang.
metadata:
  author: samber
  version: "1.2.1"
  openclaw:
    emoji: "๐Ÿ›ก"
    homepage: https://github.com/samber/cc-skills-golang
    requires:
      bins:
        - go
    install: []
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent

**Persona:** You are a defensive Go engineer. You treat every untested assumption about nil, capacity, and numeric range as a latent crash waiting to happen.

Go Safety: Correctness & Defensive Coding

Prevents programmer mistakes โ€” bugs, panics, and silent data corruption in normal (non-adversarial) code. Security handles attackers; safety handles ourselves.

Best Practices Summary

1. **Prefer generics over `any`** when the type set is known โ€” compiler catches mismatches instead of runtime panics 2. **Always use safe type assertions** โ€” for normal interfaces use comma-ok (`v, ok := x.(T)`); for reflection in Go 1.25+ prefer `reflect.TypeAssert[T](value)` over `value.Interface().(T)`. 3. **Typed nil pointer in an interface is not `== nil`** โ€” the type descriptor makes it non-nil 4. **Writing to a nil map panics** โ€” always initialize before use 5. **`append` may reuse the backing array** โ€” both slices share memory if capacity allows, silently corrupting each other 6. **Return defensive copies** from exported functions โ€” otherwise callers mutate your internals 7. **`defer` runs at function exit, not loop iteration** โ€” extract loop body to a function 8. **Integer conversions truncate silently** โ€” `int64` to `int32` wraps without error 9. **Float arithmetic is not exact** โ€” use epsilon comparison or `math/big` 10. **Design useful zero values** โ€” nil map fields panic on first write; use lazy init 11. **Use `sync.Once` for lazy init** โ€” guarantees exactly-once even under concurrency

Nil Safety

Nil-related panics are the most common crash in Go.

The nil interface trap

Interfaces store (type, value). An interface is `nil` only when both are nil. Returning a typed nil pointer sets the type descriptor, making it non-nil:

// โœ— Dangerous โ€” interface{type: *MyHandler, value: nil} is not == nil
func getHandler() http.Handler {
    var h *MyHandler // nil pointer
    if !enabled {
        return h // interface{type: *MyHandler, value: nil} != nil
    }
    return h
}

// โœ“ Good โ€” return nil explicitly
func getHandler() http.Handler {
    if !enabled {
        return nil // interface{type: nil, value: nil} == nil
    }
    return &MyHandler{}
}

Nil map, slice, and channel behavior

| Type | Index into nil | Write to nil | Len/Cap of nil | Range over nil | | ------- | -------------- | -------------- | -------------- | -------------- | | Map | Zero value | **panic** | 0 | 0 iterations | | Slice | **panic** | **panic** | 0 | 0 iterations | | Channel | Blocks forever | Blocks forever | 0 | Blocks forever |

// โœ— Bad โ€” nil map panics on write
var m map[string]int
m["key"] = 1

// โœ“ Good โ€” initialize or lazy-init in methods
m := make(map[string]int)

func (r *Registry) Add(name string, val int) {
    if r.items == nil { r.items = make(map[string]int) }
    r.items[name] = val
}

See **[Nil Safety Deep Dive](./references/nil-safety.md)** for nil receivers, nil in generics, and nil interface performance.

Slice & Map Safety

Slice aliasing โ€” the append trap

`append` reuses the backing array if capacity allows. Both slices then share memory:

// โœ— Dangerous โ€” a and b share backing array
a := make([]int, 3, 5)
b := append(a, 4)
b[0] = 99 // also modifies a[0]

// โœ“ Good โ€” full slice expression forces new allocation
b := append(a[:len(a):len(a)], 4)

Map concurrent access

Maps MUST NOT be accessed concurrently โ€” โ†’ see `samber/cc-skills-golang@golang-concurrency` for sync primitives.

See **[Slice and Map Deep Dive](./references/slice-map-safety.md)** for range pitfalls, subslice memory retention, and `slices.Clone`/`maps.Clone`.

Numeric Safety

Implicit type conversions truncate silently

// โœ— Bad โ€” silently wraps around if val > math.MaxInt32 (3B becomes -1.29B)
var val int64 = 3_000_000_000
i32 := int32(val) // -1294967296 (silent wraparound)

// โœ“ Good โ€” check before converting
if val > math.MaxInt32 || val < math.MinInt32 {
    return fmt.Errorf("value %d overflows int32", val)
}
i32 := int32(val)

Float comparison

// โœ— Bad โ€” floating point arithmetic is not exact
var a, b, c float64 = 0.1, 0.2, 0.3
a+b == c // false

// โœ“ Good โ€” use epsilon comparison
const epsilon = 1e-9
math.Abs((a+b)-c) < epsilon // true

Division by zero

Integer division by zero panics. Float division by zero produces `+Inf`, `-Inf`, or `NaN`.

func avg(total, count int) (int, error) {
    if count == 0 {
        return 0, errors.New("division by zero")
    }
    return total / count, nil
}

For integer overflow as a security vulnerability, see the `samber/cc-skills-golang@golang-security` skill section.

Resource Safety

defer in loops โ€” resource accumulation

`defer` runs at _function_ exit, not loop iteration. Resources accumulate until the function returns:

// โœ— Bad โ€” all files stay open until function returns
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close() // deferred until function exits
    process(f)
}

// โœ“ Good โ€” extract to function so defer runs per iteration
for _, path := range
Read more
Ships withcc-skills-golang

AI agent skills are reusable instruction sets that extend your coding assistant with domain-specific expertise, loaded on demand so they don't bloat your context. This repository covers Go-specific skills only (language, testing, security, observability, etc.)

Get the whole plugin

Other skills on cc-skills-golang.