agentleFS
h0rn3th0rn3t/golang-skills/skills/go-packages/SKILL.md

go-packages

Skill7 starsChanged 10 days agoApache-2.0View on GitHub

Use when creating or splitting Go packages, organizing imports or dependencies, or structuring a new Go project. Individual identifier naming belongs to go-naming.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Stop at the first rung that works:

    line 22
  2. Very specific:

    Avoid init() where possible.

    line 117
  3. Very specific:

    Call os.Exit or log.Fatal* only in main().

    line 127
  4. Very specific:

    Best practice: Use the run() pattern — extract logic into a run that returns an error, called from main() with a single exit point:

    line 132
  5. Very specific:

    Follow the project's flag naming convention.

    line 154
  6. Very specific:

    Prefer the standard flag package, which already accepts -v and --v as the same flag.

    line 158

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Go Packages and Imports

Compatibility: Baseline Go 1.27 (see COMPATIBILITY.md). The tool directive requires Go 1.24+; stdlib uuid and encoding/json/v2 Go 1.27+.

Resource Routing

  • references/IMPORTS.md - Read when grouping imports, using blank imports, dot imports, or import aliases.
  • references/PACKAGE-SIZE.md - Read when splitting packages, avoiding init, structuring main, or designing CLI flags/subcommands.
  • references/DEPENDENCIES.md - Read when running go mod init or setting the go directive, adding a module, using replace or exclude, pinning a tool, tidying, or scanning before a release.

Dependency Ladder

Normative: Before adding a module to go.mod, check the stdlib. Go 1.27 absorbed several of the most-added dependencies.

Stop at the first rung that works:

  1. Standard library — check go doc <pkg> before assuming it is missing
  2. A module already in go.mod — reuse a suitable supported API
  3. A new module, including golang.org/x/... — when the above do not meet the contract or would require materially more implementation and maintenance

Judge suitability by required semantics, supported versions, and maintenance, not line count alone. Existing project conventions take precedence; this ladder does not require replacing a working dependency in neighboring code. A dependency is a convention; a standard-library idiom is not (Write Current Go).

Commonly added modules the standard library now covers:

WasUse insteadSince
github.com/google/uuiduuid — New/NewV4/NewV7, Parse/MustParse, Nil/Max, Compare, text marshaling1.27
A third-party JSON library kept for speedencoding/json, which runs on the v2 implementation (unmarshal faster, marshal slower than on 1.26); encoding/json/v2 + encoding/json/jsontext for v2 semantics or streaming1.27
github.com/sirupsen/logrus, go.uber.org/zaplog/slog (see go-logging)1.21
github.com/pkg/errorsfmt.Errorf with %w, errors.Is, errors.AsType1.13 / 1.26
golang.org/x/exp/slices, .../mapsslices, maps1.21
A CSPRNG string helpercrypto/rand.Text1.24

encoding/json/v2 is not a drop-in replacement for encoding/json: it changes omitempty, case-matching, and error semantics. Keep v1 for existing wire formats; reach for v2 for new code or when you need jsontext streaming. Both ship in the toolchain — no build tag or GOEXPERIMENT needed on Go 1.27. For I/O forms and compatibility options, read JSON v2 at API boundaries.

github.com/google/uuid stays justified for the algorithms stdlib uuid does not have (v1, v3, v5, custom sources).


Adding and Auditing Dependencies

  • Upgrade one module per change — go get <module>@<version>, not go get -u ./... — so a failure names its cause and the revert is one line; modules that release together count as one. A minor bump needs its release notes read, a major one its migration notes, and a v0 minor may break. Minimal version selection raises the indirect requirements the new version declares: the // indirect and go.sum lines are part of the diff, and go mod graph | grep <module>@ names who pulled a surprise version. Tests run before and after; govulncheck covers the new versions.
  • A checksum mismatch is a finding. SECURITY ERROR from the go command means the bytes differ from go.sum or the checksum database: report the module, version, and both hashes. Never edit go.sum or set GOSUMDB, GONOSUMDB, or GOPRIVATE to get past it; go clean -modcache fixes only what go mod verify reports as dir has been modified.

go mod init and the go directive, the pre-add check, replace and exclude, tool pinning, tidy, and the release scan: DEPENDENCIES.md.


Package Organization

Layout

  • cmd/<binary> holds each main package; everything it imports lives elsewhere.
  • internal/ holds packages that are not API. Use it for anything you do not want to support; do not wrap the whole module in internal/pkg. A layer tree (internal/handlers, internal/services, internal/repositories, internal/models) fits one cohesive service; once several domains change independently, the same layer names move under internal/<module>/.
  • Restructuring an existing module — a god package, a global layer tree several domains share, a monolith heading for modules — is go-code-refactor's ARCHITECTURE.md: the import-graph audit, the target shapes, the staged move, and the boundary test. This skill decides where a new package goes.
  • A package named util, helper, or common is a finding: go-naming owns the rule.

Package Size

Don't split just because a file is long, to create single-type packages, or if it would create circular dependencies.


Imports

RuleGuidance
Blank imports (import _)Only in main packages or tests
import _ "embed"Any package, in the file whose //go:embed fills a string or []byte — the compiler requires it

Avoid init()

Avoid init() where possible. When unavoidable, it must be:

  1. Completely deterministic
  2. Independent of other init() ordering
  3. Free of environment state and I/O

Exit in Main

Call os.Exit or log.Fatal* only in main(). All other functions should return errors.

Why: Non-obvious control flow, untestable, defer statements skipped.

Best practice: Use the run() pattern — extract logic into a run that returns an error, called from main() with a single exit point:

func main() {
    if err := run(os.Args[1:], os.Stdout, os.Stderr); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

A CLI's run takes args []string, stdout, stderr io.Writer and builds its flag set or command tree inside: a package-level FlagSet or cobra root keeps flags and state from one test to the next.


Command-Line Flags

Advisory: Define flags only in package main.

  • Follow the project's flag naming convention; this pack defaults to snake_case (--output_dir) when none exists.
  • Libraries should accept configuration as parameters, not read flags directly — this keeps them testable and reusable
  • Prefer the standard flag package, which already accepts -v and --v as the same flag; use pflag only when POSIX/GNU conventions are required — grouped single-letter flags (-xvf) or short/long pairs (-v/--verbose)

Build Constraints and Embedded Files

  • Platform-specific code lives in _linux.go / _windows.go suffix files or behind //go:build linux; a runtime if runtime.GOOS == ... switch is the last resort. Every constrained file needs a fallback so the package still compiles under go vet ./... on any GOOS.
  • Static assets (templates, SQL, schemas) ship via //go:embed in the package that uses them — never by reading a relative path at runtime, which breaks as soon as the binary runs from another directory.
//go:embed schema/*.sql
var schemaFS embed.FS

Related Skills

  • go-naming: package names, stuttering, exported symbols.
  • go-error-handling: %w versus %v at package boundaries.
  • go-linting: goimports local prefixes, import grouping.
  • go-defensive: replacing init(), mutable globals.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.