.agents/skills/go-defensive/SKILL.md
Defensive programming patterns in Go including interface verification, slice/map copying at boundaries, time handling, avoiding globals, and defer for cleanup. Use when writing robust, production-quality Go code.
npx skillsauth add phant-app/phant go-defensiveInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Source: Uber Go Style Guide
Verify interface compliance at compile time using zero-value assertions.
Bad
type Handler struct{}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// ...
}
Good
type Handler struct{}
var _ http.Handler = (*Handler)(nil)
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// ...
}
Use nil for pointer types, slices, maps; empty struct {} for value receivers.
Source: Uber Go Style Guide
Slices and maps contain pointers. Copy at API boundaries to prevent unintended modifications.
Bad
func (d *Driver) SetTrips(trips []Trip) {
d.trips = trips // caller can still modify d.trips
}
Good
func (d *Driver) SetTrips(trips []Trip) {
d.trips = make([]Trip, len(trips))
copy(d.trips, trips)
}
Bad
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
return s.counters // exposes internal state!
}
Good
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
result := make(map[string]int, len(s.counters))
for k, v := range s.counters {
result[k] = v
}
return result
}
Source: Uber Go Style Guide, Effective Go
Use defer to clean up resources (files, locks). Avoids missed cleanup on multiple returns.
Bad
p.Lock()
if p.count < 10 {
p.Unlock()
return p.count
}
p.count++
newCount := p.count
p.Unlock()
return newCount // easy to miss unlocks
Good
p.Lock()
defer p.Unlock()
if p.count < 10 {
return p.count
}
p.count++
return p.count
Defer overhead is negligible. Only avoid in nanosecond-critical paths.
Place defer f.Close() immediately after opening a file for clarity:
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // Close sits near Open - much clearer
var result []byte
buf := make([]byte, 100)
for {
n, err := f.Read(buf[0:])
result = append(result, buf[0:n]...)
if err != nil {
if err == io.EOF {
break
}
return "", err // f will be closed
}
}
return string(result), nil // f will be closed
}
Arguments to deferred functions are evaluated when defer executes, not when the
deferred function runs:
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
// Prints: 4 3 2 1 0 (LIFO order, values captured at defer time)
Multiple defers execute in Last-In-First-Out order:
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a")) // trace() runs now, un() runs at return
fmt.Println("in a")
}
// Output: entering: a, in a, leaving: a
Source: Uber Go Style Guide
Start enums at non-zero to distinguish uninitialized from valid values.
Bad
const (
Add Operation = iota // Add=0, zero value looks valid
Subtract
Multiply
)
Good
const (
Add Operation = iota + 1 // Add=1, zero value = uninitialized
Subtract
Multiply
)
Exception: When zero is the sensible default (e.g., LogToStdout = iota).
Source: Uber Go Style Guide
Always use the time package. Avoid raw int for time values.
Bad
func isActive(now, start, stop int) bool {
return start <= now && now < stop
}
Good
func isActive(now, start, stop time.Time) bool {
return (start.Before(now) || start.Equal(now)) && now.Before(stop)
}
Bad
func poll(delay int) {
time.Sleep(time.Duration(delay) * time.Millisecond)
}
poll(10) // seconds? milliseconds?
Good
func poll(delay time.Duration) {
time.Sleep(delay)
}
poll(10 * time.Second)
When time.Duration isn't possible, include unit in field name:
Bad
type Config struct {
Interval int `json:"interval"`
}
Good
type Config struct {
IntervalMillis int `json:"intervalMillis"`
}
Source: Uber Go Style Guide
Use dependency injection instead of mutable globals.
Bad
var _timeNow = time.Now
func sign(msg string) string {
now := _timeNow()
return signWithTime(msg, now)
}
// Test requires save/restore of global
func TestSign(t *testing.T) {
oldTimeNow := _timeNow
_timeNow = func() time.Time { return someFixedTime }
defer func() { _timeNow = oldTimeNow }()
assert.Equal(t, want, sign(give))
}
Good
type signer struct {
now func() time.Time
}
func newSigner() *signer {
return &signer{now: time.Now}
}
func (s *signer) Sign(msg string) string {
now := s.now()
return signWithTime(msg, now)
}
// Test injects dependency cleanly
func TestSigner(t *testing.T) {
s := newSigner()
s.now = func() time.Time { return someFixedTime }
assert.Equal(t, want, s.Sign(give))
}
Source: Uber Go Style Guide
Embedded types leak implementation details and inhibit type evolution.
Bad
type ConcreteList struct {
*AbstractList
}
Good
type ConcreteList struct {
list *AbstractList
}
func (l *ConcreteList) Add(e Entity) {
l.list.Add(e)
}
func (l *ConcreteList) Remove(e Entity) {
l.list.Remove(e)
}
Embedding problems:
Source: Uber Go Style Guide
Always use explicit field tags for JSON, YAML, etc.
Bad
type Stock struct {
Price int
Name string
}
Good
type Stock struct {
Price int `json:"price"`
Name string `json:"name"`
// Safe to rename Name to Symbol
}
Tags make the serialization contract explicit and safe to refactor.
Source: Go Wiki CodeReviewComments (Normative)
Do not use math/rand or math/rand/v2 to generate keys, even throwaway ones. This is a security concern.
Unseeded or time-seeded random generators have predictable output:
Time.Nanoseconds() provides only a few bits of entropyUse crypto/rand instead:
import (
"crypto/rand"
)
func Key() string {
return rand.Text()
}
For text output:
crypto/rand.Text directly (preferred)encoding/hex or encoding/base64Source: Effective Go
Use panic only for truly unrecoverable situations. Library functions should avoid panic—if the problem can be worked around, let things continue rather than taking down the whole program.
Use recover to regain control of a panicking goroutine (only works inside deferred functions):
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}
Key rules:
init() if a library truly cannot set itself upFor detailed patterns including server protection and package-internal panic/recover, see references/PANIC-RECOVER.md.
| Pattern | Rule |
|---------|------|
| Interface compliance | var _ Interface = (*Type)(nil) |
| Receiving slices/maps | Copy before storing |
| Returning slices/maps | Return a copy |
| Resource cleanup | Use defer |
| Defer argument timing | Evaluated at defer, not call time |
| Enums | Start at iota + 1 |
| Time instants | Use time.Time |
| Time durations | Use time.Duration |
| Mutable globals | Use dependency injection |
| Type embedding | Use explicit delegation |
| Serialization | Always use field tags |
| Key generation | Use crypto/rand, never math/rand |
| Panic usage | Only for truly unrecoverable situations |
| Recover pattern | Use in defer; convert to error at API boundary |
go-style-core - Core Go style principlesgo-concurrency - Goroutine and channel patternsgo-error-handling - Error handling best practicesdata-ai
Beginner-first implementation planning from an approved design. Use after brainstorming, or when the user already approved requirements and wants a step-by-step Laravel implementation plan before coding.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
development
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
development
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.