From go-dev
Go development with toolchain-first verification workflow. Use whenever the user (1) writes, modifies, debugs, or reviews Go code, (2) works with go.mod, go.sum, or Go module dependencies, (3) writes or runs Go tests, benchmarks, or fuzz tests, (4) mentions go doc, go vet, go fix, go test, go build, go mod, gofmt, staticcheck, golangci-lint, govulncheck, or pprof, (5) asks about Go error handling (errors.Is/As/AsType/Join), concurrency (goroutines, channels, sync, context), interfaces, or package design, (6) encounters Go compiler errors, test failures, or race conditions, (7) profiles Go code for performance or works with PGO, (8) asks about Go best practices, code review, or idiomatic Go, (9) works with files ending in .go or _test.go, (10) wants to modernize Go code to 1.21-1.26 features. Trigger this skill proactively when the user is working in a Go codebase even if they do not explicitly ask for Go help - the toolchain-first workflow (verify APIs with go doc before coding, run go vet after) catches bugs that hallucinated signatures and skipped static analysis miss. Do NOT trigger for pure educational or comparative content about Go that does not involve writing or debugging code (e.g. tutorials for blog posts, Java vs Go decision documents, explanations of the Go scheduler for learning purposes).
How this skill is triggered — by the user, by Claude, or both
Slash command
/go-dev:go-devThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
At the start of any Go session, detect the project's Go version:
At the start of any Go session, detect the project's Go version:
go.mod and extract the go directive (e.g., go 1.26)| Version | Key additions |
|---|---|
| 1.21 | log/slog, slices, maps, min/max/clear builtins, context.AfterFunc, context.WithoutCancel, sync.OnceFunc/OnceValue, PGO auto |
| 1.22 | for i := range n, math/rand/v2, enhanced http.ServeMux routing (method+pattern), loopvar semantic change |
| 1.23 | iter.Seq/iter.Seq2, range-over-func, unique.Handle, structs.HostLayout |
| 1.24 | testing.T.Context, testing.T.Chdir, os.Root, generic type aliases, go tool runs module tools, testing/synctest, omitzero JSON tag |
| 1.25 | sync.WaitGroup.Go, testing.T.Attr, testing.T.Output, sync.Map range-over-func, os.OpenRoot |
| 1.26 | errors.AsType[T], testing.T.ArtifactDir, go test -artifacts, go fix command (21 fixers), new vet analyzers (waitgroup, hostport), new(expr) shorthand |
This is the core of this skill. Go ships a powerful toolchain - use it instead of guessing.
When about to use a stdlib or third-party API you are not certain about, verify the signature first:
go doc <package>.<Symbol> # exact function/type/method
go doc -all <package>.<Type> # full type with all methods
go doc -src <package>.<Symbol> # source code when implementation matters
Token efficiency matters: go doc fmt.Fprintf returns 5 lines. go doc fmt returns hundreds. Always use the most specific query that answers your question.
For third-party packages, they must be importable (in go.mod) before go doc works. For stdlib, it always works.
Pick the verification level that matches the scope of the change:
Full - new files, unfamiliar APIs, concurrency code, public interface changes:
gofmt -d . # format check (should produce no output)
go vet ./... # static analysis
go build ./... # compilation check
go test -race -count=1 ./... # tests with race detector, cache bypassed
Or use the bundled script: ${CLAUDE_SKILL_DIR}/scripts/go-quality-check.sh ./...
Standard - modifying existing code in patterns the project already uses:
go vet ./...
go test -count=1 -run TestRelevant ./path/to/pkg/...
Light - formatting, comments, documentation, renaming:
gofmt -d <changed-file>
Go 1.26 introduced go fix, which applies automated improvements:
go fix -diff ./... # preview changes as unified diff
go fix ./... # apply all fixes
This replaces patterns like interface{} with any, sort.Slice with slices.Sort, manual wg.Add/Done with wg.Go, and many more. Always preview with -diff first.
go test -v -run TestName -count=1 ./pkg/... # verbose, cache-bypassed
go test -race -count=1 ./... # if concurrency is involved
go test -coverprofile=c.out ./... && go tool cover -func=c.out # coverage gaps
go doc on the candidate package to verify its API before committing to itgo get <module>@latest && go mod tidygo mod why <module> to verify it is actually used| Pattern | What it returns |
|---|---|
go doc fmt | Package synopsis |
go doc fmt.Fprintf | Specific function signature and doc |
go doc -all fmt.Stringer | Full type including all methods |
go doc -src fmt.Fprintf | Source code of the function |
go doc -short fmt | One-line per symbol |
go doc -u net/http.Transport | Include unexported fields |
go doc cmd/go | Go command documentation |
| Analyzer | What it catches |
|---|---|
appends | Missing values after append |
assign | Useless assignments |
atomic | Common sync/atomic mistakes |
bools | Boolean operator mistakes |
buildtag | Invalid //go:build directives |
composites | Unkeyed composite literals |
copylocks | Locks passed by value |
defers | Common defer mistakes |
errorsas | Wrong types passed to errors.As |
hostport | Bad address format for net.Dial |
httpresponse | HTTP response handling mistakes |
loopclosure | Loop variable capture in nested functions |
lostcancel | Context cancel function not called |
printf | Printf format string mismatches |
shadow | Variable shadowing (via -vettool) |
slog | Invalid structured logging calls |
stdversion | Uses of too-new stdlib symbols |
structtag | Malformed struct tags |
tests | Mistaken test/example/benchmark signatures |
unmarshal | Non-pointer passed to unmarshal |
unusedresult | Unused results from certain calls |
waitgroup | Misuses of sync.WaitGroup |
(Run go tool vet help for the full list of all 37.)
| Fixer | What it modernizes |
|---|---|
any | interface{} -> any |
fmtappendf | []byte(fmt.Sprintf(...)) -> fmt.Appendf |
forvar | Remove redundant loop variable re-declarations |
mapsloop | Explicit map loops -> maps package calls |
minmax | if/else chains -> min/max builtins |
newexpr | Simplify with new(expr) (1.26) |
omitzero | omitempty -> omitzero for struct fields |
rangeint | 3-clause for -> for i := range n |
slicescontains | Loop searches -> slices.Contains |
slicessort | sort.Slice -> slices.Sort |
stringsbuilder | String concatenation += -> strings.Builder |
stringscut | strings.Index patterns -> strings.Cut |
stringscutprefix | HasPrefix/TrimPrefix -> CutPrefix |
stringsseq | Split/Fields ranges -> SplitSeq/FieldsSeq iterators |
testingcontext | context.WithCancel in tests -> t.Context() |
waitgroup | wg.Add(1); go func() { defer wg.Done()... } -> wg.Go(f) |
(Run go tool fix help for the full list.)
| Flag | Purpose |
|---|---|
-race | Enable race detector |
-count=1 | Bypass test cache |
-run <regex> | Run only matching tests |
-v | Verbose output |
-short | Skip long-running tests (tests check testing.Short()) |
-shuffle=on | Randomize test order |
-failfast | Stop on first failure |
-cover | Enable coverage analysis |
-coverprofile=f | Write coverage profile to file |
-coverpkg=pattern | Apply coverage to matching packages |
-bench=<regex> | Run matching benchmarks |
-benchmem | Report allocations in benchmarks |
-benchtime=5s | Benchmark duration |
-fuzz=<regex> | Run matching fuzz tests |
-timeout=10m | Test timeout (default 10m) |
-cpuprofile=f | Write CPU profile |
-memprofile=f | Write memory profile |
-artifacts | Store test artifacts in output directory (1.26+) |
| Flag | Purpose |
|---|---|
-race | Enable race detector |
-pgo=auto | Profile-guided optimization (auto uses default.pgo) |
-gcflags='-m' | Show escape analysis |
-gcflags='-S' | Show assembly output |
-ldflags='-s -w' | Strip debug info (smaller binary) |
-ldflags='-X main.version=v1.0' | Embed build-time values |
-trimpath | Remove filesystem paths from binary |
-tags=<list> | Build constraint tags |
-o <file> | Output file path |
| Command | Purpose |
|---|---|
go mod tidy | Sync go.mod/go.sum with imports |
go mod download | Download modules to cache |
go mod graph | Print module dependency graph |
go mod vendor | Create vendored copy |
go mod verify | Verify dependencies match go.sum |
go mod why <mod> | Explain why a module is needed |
go mod edit -go=1.26 | Update go directive |
| Flag | Purpose |
|---|---|
-d | Print diff (do not modify files) |
-l | List files with formatting differences |
-s | Simplify code |
-w | Write changes to files |
Check availability before use. These are not part of the Go toolchain:
| Tool | Check | Purpose |
|---|---|---|
staticcheck | which staticcheck | Extended static analysis beyond go vet |
golangci-lint | which golangci-lint | Meta-linter running 100+ linters |
govulncheck | which govulncheck | Scan dependencies for known vulnerabilities |
If unavailable, go vet covers the most critical checks. Do not block on missing external tools.
| Diagnostic | Cause | Fix | Reference |
|---|---|---|---|
declared and not used | Unused variable | Remove it or use it | - |
imported and not used | Unused import | Remove import; use _ alias only during active development | - |
cannot use X as type Y | Type mismatch | Run go doc on both types; check interface satisfaction | references/interfaces-and-design.md |
data race detected | Concurrent unsynchronized access | Use mutex, channel, or atomic; see concurrency patterns | references/concurrency.md |
err is shadowed during return | := in inner scope shadows outer err | Use = instead of := or rename inner variable | references/error-handling.md |
loop variable X captured by func literal | Pre-1.22 loop var capture | Go 1.22+ fixes this; for older: copy variable before closure | - |
possible misuse of sync.WaitGroup | Add called inside goroutine | Call Add before starting goroutine, not inside it | references/concurrency.md |
context.Background used in long-lived operation | Missing context propagation | Accept context.Context as first parameter; pass from caller | references/concurrency.md |
go directive in go.mod too old | go.mod version < required feature | Run go mod edit -go=<version> to update | references/modules-and-deps.md |
Any ioutil.* usage | Deprecated since Go 1.16 | ioutil.ReadAll -> io.ReadAll; ioutil.ReadFile -> os.ReadFile; etc. | references/modern-go.md |
| HTTP handler decodes body without size limit | DoS via unbounded request body | Wrap r.Body with http.MaxBytesReader(w, r.Body, maxBytes) | references/security.md |
http.Server{} without timeouts | Vulnerable to slowloris attacks | Set ReadTimeout, WriteTimeout, IdleTimeout | references/security.md |
These are the rules for when to use tools without being asked:
go doc <pkg>.<Symbol> before claiming any API signature you have not used in this session. This is the single most important behavior - wrong signatures waste the user's time.go vet ./... after creating new files, adding exported functions, or modifying concurrency code.gofmt -d <file> before presenting code as complete. If it produces output, the code has formatting issues.go test -race -count=1 ./... after modifying code involving goroutines, channels, shared state, or sync primitives.go directive in go.mod before suggesting features from newer versions. Use the version feature table above.go fix -diff ./... to identify modernization opportunities. Present the diff to the user before applying.ioutil (deprecated 1.16), math/rand.Seed (unnecessary since 1.20), // +build (replaced by //go:build).errors.AsType[T] on Go 1.26+.http.MaxBytesReader to prevent denial-of-service via unbounded uploads. This is easy to forget and hard to catch in code review.http.Server, always set ReadTimeout, WriteTimeout, and IdleTimeout. A server without timeouts is vulnerable to slowloris attacks.When NOT to run tools:
go test when only editing comments or documentationgo vet when the user is just asking a question, not writing codego doc for universally known functions (fmt.Println, len, append, etc.)These are the philosophical foundations. When reviewing or writing Go code, apply these as judgment calls, not rigid rules:
fmt.Errorf("doing X: %w", err). See references/error-handling.md.sync.Mutex, bytes.Buffer).user package, not a models package.defer immediately after acquiring a resource. It communicates cleanup intent right where the resource is opened.Open the reference file that matches the question. Load only one at a time.
errors.Is/As/AsType, errors.Join) -> references/error-handling.mdreferences/concurrency.mdreferences/testing.mdgo fix guide) -> references/modern-go.mdreferences/performance.mdreferences/interfaces-and-design.mdreferences/modules-and-deps.mdreferences/security.mdFor a problem-based router ("I need to..."), see references/_index.md.
Before declaring work done on Go code:
gofmt -d . produces no outputgo vet ./... produces no diagnosticsgo build ./... succeedsgo test -race -count=1 ./... passesioutil, math/rand.Seed, // +build)%w where callers need errors.Is/errors.Ascontext.Context is threaded through where cancellation mattershttp.MaxBytesReader for request body limitsReadTimeout, WriteTimeout, IdleTimeout setgo mod tidy has been run if dependencies changedGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub pszypowicz/claude-skills --plugin go-dev