From google-go-style
Applies Google Go Style Guide rules for naming, errors, and API design when writing or reviewing Go code.
How this skill is triggered — by the user, by Claude, or both
Slash command
/google-go-style:google-go-styleThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
_Derived from the [Google Go Style Guide](https://google.github.io/styleguide/go/), © Google LLC, licensed [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/). This skill is a derivative digest, not a verbatim reproduction._
Derived from the Google Go Style Guide, © Google LLC, licensed CC-BY 3.0. This skill is a derivative digest, not a verbatim reproduction.
Codifies the Google Go Style Guide (canonical + normative + best-practices) into actionable rules.
The full guide is large. This file holds only what should be in your head on every Go change. For deeper rules, load the matching references/*.md. For unusual situations, WebFetch the source — the guide is normative.
Apply on every line of Go you write or review.
Get prefix on getters. User(), not GetUser(). Exception: the underlying concept is "get" (HTTP GET).bytes.Buffer, not bytes.BytesBuffer. widget.New, not widget.NewWidget.this, self, me, or _ (unless unused). func (c *Config), not func (config *Config).URL, ID, HTTP, DB. Exported: UserID, ServeHTTP. Unexported: userID, urlPath. Never Url, Id, Http.Stub / Fake / Spy / Mock (or describe behaviour: AlwaysCharges, AlwaysDeclines).util / common / helper / model package names. They invite import renames at every call site. Name by what the package provides.*_test.go test/benchmark/example names, and packages imported only by generated code).i, c, db for tight loops; userCount, pollInterval for file scope. Don't drop letters to save typing (Sandbox, not Sbx).fmt.Errorf("doing X: %w", err) — wrap with %w at the end, so the chain prints newest→oldest as outer: middle: inner.%w only when callers need errors.Is / errors.As on the underlying error. Otherwise use %v.%w at the start is for sentinel wrappers only: fmt.Errorf("%w: invalid header", ErrParse). Category first, details after.ErrFoo at package level: var ErrNotFound = errors.New("not found").os.Open errors already include the path — fmt.Errorf("could not open settings.txt: %v", err) is wrong; use a higher-level annotation: fmt.Errorf("launch codes unavailable: %v", err).return err.\n. fmt.Errorf("something bad happened"), not "Something bad happened.".status.Errorf(codes.X, ...) rather than wrapping internal errors raw with %w.error is the last return value. A function taking context.Context should usually return error.For more, see references/errors.md.
error and multiple return values.log.Fatal over panic for terminal conditions in main / init. Fatal does not run deferred functions; that's the point.error at the API edge with a top-level defer recover() that re-panics on unknown payloads.MustX is for package-level vars and tests only. MustParse, template.Must. Not for runtime user input.recover() to suppress crashes. Corrupted state propagates further; better is monitoring + crash + fix.panic("unreachable") after log.Fatalf is the idiom — the compiler doesn't know Fatal doesn't return.For more, see references/panics.md.
assert.Equal, require.NotNil). Use if got != want { t.Errorf(...) }. For complex types: cmp.Equal / cmp.Diff from go-cmp.t.Error over t.Fatal by default — keep going, report all failures in one run. t.Fatal only when continuing is meaningless (setup failed, cascading errors would mislead).t.Run subtests: use t.Fatal to skip just that case. Outside subtests in a table loop: t.Error + continue.t.Fatal (FailNow, Fatalf, SkipNow) from a goroutine other than the test's own. Use t.Error from worker goroutines; t.Fatal only after wg.Wait() from the main goroutine.{name: "empty", input: "", want: ""}.t.Helper() + t.Fatalf. This makes the failure point to the test line, not the helper line.YourFunc(%v) = %v, want %v — function name, inputs, got, want, in that order.For more, see references/tests.md.
:= for non-zero init, var x T for zero values. i := 42, but var coords Point (not coords := Point{}).var t []string, not t := []string{} for empty slices. Empty slice and nil slice behave the same for len, cap, range, append.new(T) vs &T{}: both are fine for zero values; new reads as "zero value placeholder", &T{} is more common when fields are filled.== on nil for slices. Use len(s) == 0. APIs must not distinguish nil from empty.+ for 2–3 short literals; fmt.Sprintf for formatting; strings.Builder in loops; text/template for templates.make([]T, 0, n) / make(map[K]V, n) only when n is known and the code is performance-sensitive. Default to zero-init.if *x { ctx, cancel := ...; } shadows ctx outside the if. Use ctx, cancel = ... with = and var cancel func() declared above.For more, see references/strings-and-vars.md.
context.Context is the first parameter. func F(ctx context.Context, ...) error. Even in test helpers.context.Context in a struct. Pass it through every method that needs it.<-chan T for receive-only, chan<- T for send-only.context.Context in option structs....Option) only when most callers pass nothing, options need parameters, or third parties define options.*string, *io.Reader parameters "to save bytes". Pass values; use pointers for large structs and protobuf messages.For more, see references/api-design.md.
// Encode writes the JSON encoding of req to w.Close, Stop, deferred resources).ctx.Done cancels the function — that's the contract of context.Context.For more, see references/documentation.md.
internal/ for packages not part of the public API.util / common / helper. Name by domain.main or tests.import . ever (in Google codebase).pb suffix: foopb "path/to/foo_go_proto".For more, see references/package-layout.md.
%v vs %w when wrapping an error| Situation | Choose | Why |
|---|---|---|
Caller will errors.Is / errors.As to inspect the chain | %w | Preserves type/sentinel through the chain |
| Crossing an external boundary (RPC, IPC, storage); caller wants canonical codes | %v (or status.Errorf) | Don't leak internal error types over the wire |
| Logging or human-display only; no programmatic inspection | %v | %w adds chain semantics nobody uses |
| Same error is logged here AND returned upward | %v | Wrapping a logged-then-returned error confuses the chain |
Sentinel categorisation first (ErrParse-style) | %w at start: "%w: detail" | Reader sees the category first |
| Adding context around a wrapped error (the common case) | %w at end: "context: %w" | Chain prints newest→oldest naturally: outer: middle: inner |
| Underlying error already carries this info | nothing — return err | Wrapping without adding info is noise |
| Just propagating without analysis | nothing — return err | Don't wrap for the sake of wrapping |
| Situation | Choose | Why |
|---|---|---|
| ≤ 3 parameters, all required, all distinct types | Positional args | Smallest mechanism |
| Many parameters, most callers set most of them | Option struct (last param) | Self-documenting field names; grows without breaking call sites |
| Many parameters, most callers set none | Variadic options (...Option) | Zero overhead at simple call sites |
| Options need failure validation | Variadic options returning error | Can't validate in struct construction |
| Third-party packages must define options | Variadic options with exported Option type | Struct fields can't be extended |
| Same option set used by multiple functions | Option struct | Reuse + share + write helpers on the struct |
context.Context | First positional arg, never in option struct | Convention |
panic vs log.Fatal vs error return| Situation | Choose | Why |
|---|---|---|
| Library detects normal failure | error return | Caller decides |
| Library detects an "impossible" invariant violation | error return or log.Fatal | Caller can't recover anyway |
Bad flag/config in main / init | log.Exit (no stack) | Stack trace useless; user wants the message |
| Internal package consistency check that has been verified by tests | log.Fatal | More reliable than panic (no defer deadlock risk) |
Parser internals that always have a matching recover at the API edge | panic of a private type | Plumbing errors through deep recursion is noise |
| Package-level var initializer needs a value derived from a fallible call | MustX (panics) | Init-time only; init cannot return errors |
| HTTP handler crashes mid-request | never recover() to mask it | State is corrupted; let the process crash and restart |
t.Error vs t.Fatal vs t.Errorf + continue| Situation | Choose |
|---|---|
Multiple independent assertions in one Test*, all should run | t.Error / t.Errorf for each |
| Setup failure — rest of test cannot proceed | t.Fatal / t.Fatalf |
| First failure makes subsequent assertions misleading (e.g. encoded ≠ expected, can't decode meaningfully) | t.Fatalf then continue with t.Errorf |
| Table loop without subtests, this case is broken | t.Errorf + continue |
Inside t.Run(...) subtest, this case is broken | t.Fatal (skips this subtest only) |
| Worker goroutine inside a test | t.Errorf only (NEVER t.Fatal) |
| Test helper called from main test goroutine | t.Helper() + t.Fatalf is fine |
| Situation | Choose | Example |
|---|---|---|
| Initializing with a known non-zero value | := | i := 42 |
| Need a zero value, ready for use | var x T | var coords Point, var s []string |
Need a *T to a zero value | new(T) or &T{} | msg := new(pb.Bar) |
Need a *T to a value with fields | &T{...} | c := &Config{Port: 8080} |
| Empty slice for return / accumulation | var s []T | not s := []T{} |
| Empty map (must be initialized to write) | make(map[K]V) or map[K]V{} | nil map can be read but not written |
| Pre-sized slice/map (perf-sensitive, size known) | make([]T, 0, n) / make(map[K]V, n) | Don't over-pre-allocate |
Load on demand:
references/errors.md — detailed error structure, wrapping rules, sentinels, RPC boundaries, errors.Is/As.references/naming.md — receivers, initialisms, repetition, test doubles, util-package antipattern, shadowing.references/api-design.md — option struct vs variadic options, channel direction, DI, interfaces, generics.references/tests.md — Test funcs vs helpers, table-driven, subtests, goroutines, acceptance testing, cmp.references/panics.md — when allowed (invariants, parsers with recover, init), when forbidden, log.Fatal vs log.Exit.references/documentation.md — godoc conventions, what to document, what to skip, contexts, errors, cleanup.references/package-layout.md — package size, file structure, imports, internal/, side-effect imports.references/strings-and-vars.md — concatenation choices, zero values, size hints, new vs &T{}, shadowing.When in doubt, especially for unusual situations, WebFetch the source section before writing the code. The Google Go Style Guide is the normative authority — this skill is a digest, not a replacement.
For Go fundamentals not covered here, read Effective Go.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub cicdteam/google-go-style --plugin google-go-style