go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization, guard clauses, early returns, the unified for loop, range over slices/maps/strings/channels, parallel assignment, labeled break, and `_` for discards
Claude Code
$ npx -y skills add muratmirgun/gophers --skill go-control-flow --agent claude-code
Codex
$ npx skills add muratmirgun/gophers --skill go-control-flow -a codex -y
Cursor
$ npx skills add muratmirgun/gophers --skill go-control-flow -a cursor -y
Gemini CLI
$ npx skills add muratmirgun/gophers --skill go-control-flow -a gemini-cli -y
GitHub Copilot
$ npx skills add muratmirgun/gophers --skill go-control-flow -a github-copilot -y
OpenCode
$ npx skills add muratmirgun/gophers --skill go-control-flow -a opencode -y
Windsurf
$ npx skills add muratmirgun/gophers --skill go-control-flow -a windsurf -y
name: go-control-flow description: "Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization, guard clauses, early returns, the unified for loop, range over slices/maps/strings/channels, parallel assignment, labeled break, and `_` for discards and side-effect imports. Apply proactively to any new if/for/switch, even when the user does not mention scoping or shadowing. Does not cover error-flow specifics (see go-error-handling)." license: MIT compatibility: "Designed for Claude Code or similar AI coding agents. Plain Go (any supported version)." allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
Go gives you `if`, `for`, and `switch` — and one looping construct that covers them all. The idioms are small but strict: scope variables tightly, return early, keep the happy path unindented.
1. **Scope variables with if-init when they live only for the check.** `if x, err := f(); err != nil { ... }`. 2. **Guard clauses over nested `else`.** When the `if` body returns/breaks/continues, drop the `else`. 3. **`:=` redeclares only in the same scope.** In an inner scope it shadows — a frequent bug source. 4. **One `for`, three forms.** Condition-only (while), three-clause (C-style), and infinite (`for {}`). 5. **`range` over string yields runes**, over map yields **non-deterministic order**, over channel **drains until closed**. 6. **`break` inside `switch` only breaks the switch.** Use a labeled `break` to exit the enclosing `for`. 7. **The blank identifier `_` discards, but never errors.** Silent error dropping is a bug.
| Situation | Use | |---|---| | New variable, scoped to the check | `if v, err := f(); err != nil` | | Reusing an outer variable | plain `=` (avoid `:=` to prevent shadowing) | | At least one new + reuse of outer | `:=` is fine (same scope only) | | Wanted zero value | `var x T` |
if err := file.Chmod(0664); err != nil {
return err
}If `err` is needed past the `if`, declare it separately:
x, err := f()
if err != nil {
return err
}
// use x freelyf, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)Never bury the success path inside `else`.
// Bug: inner ctx never escapes the if block
if *shortenDeadlines {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
}
// Fix: assign with =, declaring cancel separately
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()> Read [references/blank-identifier.md](references/blank-identifier.md) for `_` use cases (interface checks, side-effect imports, multi-return discards).
// Condition-only (Go's "while")
for x > 0 { x = process(x) }
// Infinite
for {
if done() { break }
}
// Three-clause
for i := 0; i < n; i++ { ... }for i, v := range slice { ... } // index + value
for k, v := range myMap { ... } // non-deterministic order
for i, r := range "héllo" { ... } // i is byte offset; r is rune
for v := range ch { ... } // drains until closedGo has no comma operator. Use parallel assignment instead:
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}`++` and `--` are statements, not expressions — they cannot appear inside a parallel assignment.
Loop:
for _, v := range items {
switch v.Type {
case "done":
break Loop // breaks the for, not just the switch
}
}> Read [references/switch-patterns.md](references/switch-patterns.md) for expression-less switches, comma cases, fallthrough, and type switches.
| Anti-pattern | Why it hurts | Do this instead | |---|---|---| | `} else { return ... }` after a returning `if` | Pointless nesting | Drop `else`; let the happy path stay flat | | `if _, err := f(); err == nil { ... }` then use the value | Value is out of scope | Move the `:=` outside the `if` | | `:=` in inner scope reassigning outer var | Silently shadows | Use `=` (declare the new locals separately) | | Iterating a map and relying on order | Order is randomized | Sort keys explicitly | | `break` inside `switch` expecting to exit `for` | Only exits switch | Use labeled `break Label` | | `_ = doSomething()` to silence an error | Real failures vanish | Handle it or document why |
26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by…
Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function…
Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker…
Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation,…
Use when choosing or operating on Go slices, maps, arrays, strings, or container/* types — including slice internals, capacity…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.