go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Use when creating Go packages, organizing imports, managing dependencies, or structuring a Go project. Covers meaningful package names, package size, import grouping (stdlib first, then external), blank/dot imports, the run() pattern in main, init() restrictions, and CLI flag
Claude Code
$ npx -y skills add muratmirgun/gophers --skill go-packages --agent claude-code
Codex
$ npx skills add muratmirgun/gophers --skill go-packages -a codex -y
Cursor
$ npx skills add muratmirgun/gophers --skill go-packages -a cursor -y
Gemini CLI
$ npx skills add muratmirgun/gophers --skill go-packages -a gemini-cli -y
GitHub Copilot
$ npx skills add muratmirgun/gophers --skill go-packages -a github-copilot -y
OpenCode
$ npx skills add muratmirgun/gophers --skill go-packages -a opencode -y
Windsurf
$ npx skills add muratmirgun/gophers --skill go-packages -a windsurf -y
name: go-packages description: "Use when creating Go packages, organizing imports, managing dependencies, or structuring a Go project. Covers meaningful package names, package size, import grouping (stdlib first, then external), blank/dot imports, the run() pattern in main, init() restrictions, and CLI flag conventions. Apply proactively when starting a new module or splitting a growing codebase, even if the user did not explicitly ask about package layout. Does not cover identifier naming inside packages (see go-naming)." 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:*)
A package is a unit of meaning, not a folder of files. Name it for what it provides, keep imports tidy, and put startup logic where it belongs.
1. **Package names describe what the package provides.** `util`, `helper`, `common`, `misc` are not names. 2. **Imports are grouped: stdlib first, then external.** `goimports` will keep this honest. 3. **Avoid `init()`** — and when unavoidable, keep it deterministic and I/O-free. 4. **`os.Exit` / `log.Fatal` only inside `main`.** Library code returns errors. 5. **Use the `run()` pattern** so `main` has a single exit point and deferred cleanup runs. 6. **CLI flags belong in `package main`.** Libraries take configuration as parameters. 7. **Blank imports** belong in `main` or tests. **Dot imports** are essentially never appropriate.
| Question | If "yes" | |---|---| | Can you state the package's purpose in one sentence? | Probably right-sized | | Do its files never share unexported symbols? | Likely two packages glued by directory | | Do distinct caller groups touch distinct files? | Split along caller boundaries | | Is the godoc index so long callers cannot find things? | Split for discoverability | | Does splitting create import cycles? | Don't split |
> Read [references/package-layout.md](references/package-layout.md) when deciding how to split a growing package, organizing `cmd/`, `internal/`, or designing a library API surface.
// Good — meaningful db := spannertest.NewDatabaseFromFile(...) _, err := f.Seek(0, io.SeekStart) // Bad — vague db := test.NewDatabaseFromFile(...) _, err := f.Seek(0, common.SeekStart)
Generic words may appear as part of a name (`stringutil`, `iotest`) but not as the whole name. Match the package to a concept the caller already knows.
import (
"fmt"
"os"
"github.com/foo/bar"
"rsc.io/goversion/version"
)| Rule | Guidance | |---|---| | Group order | stdlib, then external; extended order may also separate protos and side-effect imports | | Renaming | Avoid unless there is a collision; rename the more-local import | | Blank import (`import _`) | Only `main` and tests | | Dot import (`import .`) | Effectively never; rare in test files for circular deps |
> Read [references/imports-and-main.md](references/imports-and-main.md) for extended import grouping, proto `pb` suffixes, the `run()` pattern, and CLI flag conventions.
When you must use `init()`, make it:
1. Deterministic — same result every run. 2. Independent of the order of other `init()`s. 3. Free of environment state (env vars, working dir, args). 4. Free of I/O (filesystem, network, syscalls).
Acceptable uses:
If your `init` reads a file or calls a network API, refactor it into an explicit `Setup()` the caller invokes.
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
// all the real work
return nil
}Why:
func main() {
outputDir := flag.String("output_dir", ".", "directory for output files")
flag.Parse()
if err := mylib.Generate(*outputDir); err != nil {
log.Fatal(err)
}
}> Read [references/init-and-globals.md](references/init-and-globals.md) for the boundaries between safe init-time computation, mutable globals, and dependency injection.
| Anti-pattern | Why it hurts | Do this instead | |---|---|---| | `package util` | Meaningless name; import conflicts | Name after the concept | | One huge package with 50 files | Hard to navigate, slow builds | Split by responsibility | | `init()` reads config from disk | Side effect at import time | Explicit `Setup()` in `main` | | `log.Fatal` in library code | Skips defers, untestable | Return an error | | `os.Exit` in a request handler | Same — plus crashes the server | Return an error to the framework | | `import _ "pkg"` in a library | Side effects on every importer | Register explicitly | | `import . "pkg"` to "save typing" | Tools lose track of where names come from | Use the package qualifier | | Library reads a flag at import time | Untestable, non-reusable | Accept config as parameter |
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 writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization,…
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.