← GophersCONTENT HISTORY

Update to Gophers

Snapshot Sep 30, 2026 · 23:14 UTC · version 0.1.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "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).",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 227
    },
    {
      "relative_path": "references/imports-and-main.md",
      "size_in_bytes": 3047
    },
    {
      "relative_path": "references/init-and-globals.md",
      "size_in_bytes": 3045
    },
    {
      "relative_path": "references/package-layout.md",
      "size_in_bytes": 2711
    }
  ],
  "name": "go-packages",
  "skill_md_contents": "---\nname: go-packages\ndescription: \"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).\"\nlicense: MIT\ncompatibility: \"Designed for Claude Code or similar AI coding agents. Plain Go (any supported version).\"\nallowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)\n---\n\n# Go Packages and Imports\n\nA 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.\n\n## Core Rules\n\n1. **Package names describe what the package provides.** `util`, `helper`, `common`, `misc` are not names.\n2. **Imports are grouped: stdlib first, then external.** `goimports` will keep this honest.\n3. **Avoid `init()`** — and when unavoidable, keep it deterministic and I/O-free.\n4. **`os.Exit` / `log.Fatal` only inside `main`.** Library code returns errors.\n5. **Use the `run()` pattern** so `main` has a single exit point and deferred cleanup runs.\n6. **CLI flags belong in `package main`.** Libraries take configuration as parameters.\n7. **Blank imports** belong in `main` or tests. **Dot imports** are essentially never appropriate.\n\n## Decision: How to Split a Package\n\n| Question | If \"yes\" |\n|---|---|\n| Can you state the package's purpose in one sentence? | Probably right-sized |\n| Do its files never share unexported symbols? | Likely two packages glued by directory |\n| Do distinct caller groups touch distinct files? | Split along caller boundaries |\n| Is the godoc index so long callers cannot find things? | Split for discoverability |\n| Does splitting create import cycles? | Don't split |\n\n> 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.\n\n## Naming Packages\n\n```go\n// Good — meaningful\ndb := spannertest.NewDatabaseFromFile(...)\n_, err := f.Seek(0, io.SeekStart)\n\n// Bad — vague\ndb := test.NewDatabaseFromFile(...)\n_, err := f.Seek(0, common.SeekStart)\n```\n\nGeneric 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.\n\n## Imports\n\n```go\nimport (\n    \"fmt\"\n    \"os\"\n\n    \"github.com/foo/bar\"\n    \"rsc.io/goversion/version\"\n)\n```\n\n| Rule | Guidance |\n|---|---|\n| Group order | stdlib, then external; extended order may also separate protos and side-effect imports |\n| Renaming | Avoid unless there is a collision; rename the more-local import |\n| Blank import (`import _`) | Only `main` and tests |\n| Dot import (`import .`) | Effectively never; rare in test files for circular deps |\n\n> 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.\n\n## Avoid `init()`\n\nWhen you must use `init()`, make it:\n\n1. Deterministic — same result every run.\n2. Independent of the order of other `init()`s.\n3. Free of environment state (env vars, working dir, args).\n4. Free of I/O (filesystem, network, syscalls).\n\nAcceptable uses:\n\n- Precomputing a constant that cannot fit in a single expression.\n- Registering pluggable hooks (`database/sql` drivers).\n\nIf your `init` reads a file or calls a network API, refactor it into an explicit `Setup()` the caller invokes.\n\n## Exit Only in `main`\n\n```go\nfunc main() {\n    if err := run(); err != nil {\n        log.Fatal(err)\n    }\n}\n\nfunc run() error {\n    // all the real work\n    return nil\n}\n```\n\nWhy:\n\n- `log.Fatal` and `os.Exit` skip `defer`. Anywhere except `main`, that means leaked files, half-flushed buffers, undeleted temp dirs.\n- The `run()` pattern gives you one place to log a clean error and one place to set the exit code.\n\n## CLI Flags\n\n- Define flags in `package main`.\n- Flag names use `snake_case`: `--output_dir`, not `--outputDir`.\n- Libraries accept configuration through function parameters, never reach for `flag.Lookup`.\n\n```go\nfunc main() {\n    outputDir := flag.String(\"output_dir\", \".\", \"directory for output files\")\n    flag.Parse()\n    if err := mylib.Generate(*outputDir); err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\n> Read [references/init-and-globals.md](references/init-and-globals.md) for the boundaries between safe init-time computation, mutable globals, and dependency injection.\n\n## Anti-Patterns\n\n| Anti-pattern | Why it hurts | Do this instead |\n|---|---|---|\n| `package util` | Meaningless name; import conflicts | Name after the concept |\n| One huge package with 50 files | Hard to navigate, slow builds | Split by responsibility |\n| `init()` reads config from disk | Side effect at import time | Explicit `Setup()` in `main` |\n| `log.Fatal` in library code | Skips defers, untestable | Return an error |\n| `os.Exit` in a request handler | Same — plus crashes the server | Return an error to the framework |\n| `import _ \"pkg\"` in a library | Side effects on every importer | Register explicitly |\n| `import . \"pkg\"` to \"save typing\" | Tools lose track of where names come from | Use the package qualifier |\n| Library reads a flag at import time | Untestable, non-reusable | Accept config as parameter |\n\n## Verification Checklist\n\n- [ ] Package name is concrete and unambiguous\n- [ ] Imports are grouped (stdlib first), `goimports` clean\n- [ ] No `init()` performs I/O or depends on env state\n- [ ] `main` is a single `if err := run(); err != nil { log.Fatal(err) }`\n- [ ] No `os.Exit` / `log.Fatal*` outside `main`\n- [ ] Flags are defined only in `package main`\n- [ ] No `import .` and no blank import outside `main`/tests\n- [ ] Package's purpose fits in one sentence\n\n## References\n\n- [references/package-layout.md](references/package-layout.md) — splitting packages, `cmd/`, `internal/`, public API surface\n- [references/imports-and-main.md](references/imports-and-main.md) — extended import grouping, the `run()` pattern, flag conventions\n- [references/init-and-globals.md](references/init-and-globals.md) — when `init` is acceptable, mutable globals, DI\n"
}

SHA-256 of public snapshot: 1148e7bf1ba7298c8440e75df160f96098639ca24c352f464be76dfcb4328275