Go packages
26 production-grade Go skills for Claude Code, Gemini CLI, and opencode.
npx -y skills add muratmirgun/gophers --skill go-packagesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
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).
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.5 KB, as published. Nobody here has run it
Go Packages and Imports
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.
Core Rules
- Package names describe what the package provides.
util,helper,common,miscare not names. - Imports are grouped: stdlib first, then external.
goimportswill keep this honest. - Avoid
init()— and when unavoidable, keep it deterministic and I/O-free. os.Exit/log.Fatalonly insidemain. Library code returns errors.- Use the
run()pattern somainhas a single exit point and deferred cleanup runs. - CLI flags belong in
package main. Libraries take configuration as parameters. - Blank imports belong in
mainor tests. Dot imports are essentially never appropriate.
Decision: How to Split a Package
| 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 when deciding how to split a growing package, organizing
cmd/,internal/, or designing a library API surface.
Naming Packages
// 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.
Imports
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 for extended import grouping, proto
pbsuffixes, therun()pattern, and CLI flag conventions.
Avoid init()
When you must use init(), make it:
- Deterministic — same result every run.
- Independent of the order of other
init()s. - Free of environment state (env vars, working dir, args).
- Free of I/O (filesystem, network, syscalls).
Acceptable uses:
- Precomputing a constant that cannot fit in a single expression.
- Registering pluggable hooks (
database/sqldrivers).
If your init reads a file or calls a network API, refactor it into an explicit Setup() the caller invokes.
Exit Only in main
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
// all the real work
return nil
}
Why:
log.Fatalandos.Exitskipdefer. Anywhere exceptmain, that means leaked files, half-flushed buffers, undeleted temp dirs.- The
run()pattern gives you one place to log a clean error and one place to set the exit code.
CLI Flags
- Define flags in
package main. - Flag names use
snake_case:--output_dir, not--outputDir. - Libraries accept configuration through function parameters, never reach for
flag.Lookup.
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 for the boundaries between safe init-time computation, mutable globals, and dependency injection.
Anti-Patterns
| 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 |
Verification Checklist
- Package name is concrete and unambiguous
- Imports are grouped (stdlib first),
goimportsclean - No
init()performs I/O or depends on env state -
mainis a singleif err := run(); err != nil { log.Fatal(err) } - No
os.Exit/log.Fatal*outsidemain - Flags are defined only in
package main - No
import .and no blank import outsidemain/tests - Package's purpose fits in one sentence
References
- references/package-layout.md — splitting packages,
cmd/,internal/, public API surface - references/imports-and-main.md — extended import grouping, the
run()pattern, flag conventions - references/init-and-globals.md — when
initis acceptable, mutable globals, DI