AgentStack
SKILL verified MIT Self-run

Golang

skill-xobotyi-cc-foundry-golang · by xobotyi

>-

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-xobotyi-cc-foundry-golang

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Golang? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Go

Simplicity is the highest Go virtue. Resist abstraction until the cost of not abstracting is proven.

References

Extended examples, code patterns, and detailed rationale for the rules below live in ${CLAUDE_SKILL_DIR}/references/.

  • [${CLAUDE_SKILL_DIR}/references/idioms.md] — Naming, declarations, interfaces, receivers, configuration,

embedding: extended code examples for each idiom, Go/bad vs good comparisons, decision criteria tables

  • [${CLAUDE_SKILL_DIR}/references/gotchas.md] — Variable shadowing, defer traps, slice mutation, strings, copy

safety: annotated code showing each pitfall with fix patterns, global state examples

  • [${CLAUDE_SKILL_DIR}/references/errors.md] — Error creation, wrapping, Is/As, structured errors (golib/e): error

type decision tree, golib/e API (sentinels, fields, logging), wrapping context examples

  • [${CLAUDE_SKILL_DIR}/references/concurrency.md] — Goroutines, channels, context, sync, errgroup, data races:

worker lifecycle patterns, pipeline/fan-out/fan-in code, data race scenarios with fixes

  • [${CLAUDE_SKILL_DIR}/references/testing.md] — Table tests, subtests, assertions, test doubles, benchmarks: full

table-test template, testify usage, parallel subtests, httptest/iotest utilities

  • [${CLAUDE_SKILL_DIR}/references/structure.md] — Project layout, packages, imports, file organization: package

naming examples, import grouping, backward-incompatible change staged workflow

Naming

Variables — The Distance Rule

Name length scales with scope distance.

  • Loop index — single letter: i, j, k
  • Short function local — 1-3 chars: r (reader), b (buffer), ctx
  • Function parameter — short but clear: name, path, opts
  • Package-level — descriptive: defaultTimeout, maxRetries
  • Exported — self-documenting: ErrNotFound, DefaultClient

Receivers

Use 1-2 letter type abbreviation: c for Client, s for Server. Never self, this, me. Be consistent across all methods of a type.

Initialisms

All-caps for known initialisms: URL, HTTP, ID, API, SQL, XML. In mixed identifiers: userID, httpClient, xmlHTTPRequest.

Packages

  • Short, lowercase, singular: user, http, auth
  • Named by what they provide, not what they contain
  • Never util, common, misc, shared, helpers, types
  • Callers use package name as prefix — don't stutter: widget.New() not widget.NewWidget()

Getters and Setters

No Get prefix on getters. Setter uses Set prefix: u.Name() not u.GetName(), u.SetName(n).

Interface Names

One-method interfaces use method name plus -er: Reader, Writer, Formatter, Stringer. Honor canonical names — if your type has String() string, call it String, not ToString.

Constants

MixedCaps only — never ALL_CAPS or K prefix. Name by role, not value. If a constant has no role beyond its value, don't define it. const MaxRetries = 12 (good) vs const Twelve = 12 (bad).

Unexported Globals

Plain lowercase: defaultPort, maxRetries. Error values use err prefix: errNotFound.

Avoid Repetition

  • Package name is part of every qualified reference: widget.New() not widget.NewWidget()
  • Don't encode type in name: var users int not var numUsers int
  • Strip context obvious from scope: method on *Project uses Name() not ProjectName()

Interfaces

  • Consumer-side. Define where used, not where implemented. Producers return concrete types.
  • No premature interfaces. Wait for a concrete need. Don't define "for mocking."
  • Accept interfaces, return structs.
  • Never pointer-to-interface. Interfaces are already reference types.
  • Small interfaces. Prefer 1-3 methods. io.Reader (1 method) is more powerful than any 10-method interface.
  • Compile-time verification. var _ http.Handler = (*Handler)(nil).

Receivers

Pointer vs Value

Use pointer receiver when: method mutates receiver, receiver contains sync.Mutex or similar, receiver is a large struct, or in doubt (default to pointer).

Use value receiver when: receiver is a small immutable value type (like time.Time), receiver is a map/func/chan (already reference types), all fields are value types with no mutability needs.

Never mix receiver types on a single type.

Maps, Funcs, and Channels

Already reference types. Never use pointers to them: func process(m map[string]int) not func process(m *map[string]int).

Context

  • First parameter. func Foo(ctx context.Context, ...).
  • Never store in structs. Pass through call chains explicitly.
  • context.Background() only at the top level — in main() or test setup.
  • Never include context.Context in option structs — pass as separate parameter.

Declarations

Variable Style

  • var for zero values: var s string, var mu sync.Mutex
  • := for initializations: s := "hello", n := computeSize()
  • Top-level: use var, omit type if obvious: var defaultPort = 8080
  • Specify type when it differs from expression: var e error = myError{}

Slices

  • Nil slices are valid and preferred: var s []string
  • Non-nil zero-length only when JSON encoding matters: s := []string{} (nil encodes as null, empty slice as [])
  • Check empty: len(s) == 0, not s == nil
  • Pre-allocate when size known: make([]T, 0, n)

Maps

  • make(map[K]V) for programmatic population; make(map[K]V, n) with capacity hint
  • Literal for fixed content: map[string]int{"a": 1, "b": 2}

Structs

  • Always use field names in literals
  • Omit zero-value fields unless they provide meaningful context
  • Zero-value struct: var user User
  • Pointer: &T{} over new(T)

Enums

Start iota at 1 to distinguish from zero-value (unless zero-value has meaning): const ( StatusActive Status = iota + 1; ... ).

Named Result Parameters

Use when they disambiguate or document caller obligations. Don't use just to enable naked returns, or when the name repeats the type.

Functions

  • Synchronous default. Let callers add concurrency.
  • Return errors, never exit. Only main() calls os.Exit/log.Fatal.
  • defer for cleanup. Always.
  • Early return on error. Happy path at minimum indentation.
  • Accept io.Reader, not filenames. Improves reusability and testability.
  • Close transient resources. defer r.Body.Close(), defer rows.Close(), defer f.Close().

Error Handling

Core Rules

  • Always check errors. Never discard with _.
  • Handle once. Log OR return — never both. If logging, degrade gracefully (don't return the error). If returning,

wrap with context and let the caller decide.

  • Wrap with context. Prefer structured errors when the project uses them: ErrNotFound.Wrap(err) or

e.NewFrom("context", err). Standard fallback: fmt.Errorf("context: %w", err). Avoid "failed to" prefix in both.

  • Error strings: lowercase, no trailing punctuation. They compose: "read config: open file: permission denied".
  • Don't panic. Return errors. Reserve panic for truly irrecoverable states.
  • Use errors.Is/errors.As — never == or direct type assertion on wrapped errors.

Error Creation

  • No match needed, static messageerrors.New("not found")
  • No match needed, dynamic messagefmt.Errorf("file %q missing", name)
  • Caller must match, static message — exported var ErrNotFound = errors.New(...)
  • Caller must match, dynamic message — custom error type with Error() method

Sentinel Errors

Naming: exported ErrXxx, unexported errXxx. Always wrap sentinels before returning so callers use errors.Is, not ==.

Custom Error Types

Naming: exported XxxError, unexported xxxError. Implement Error() string. Callers match with errors.As.

Structured Errors (golib/e)

When a project uses a structured error package like golib/e, prefer it consistently over fmt.Errorf. See ${CLAUDE_SKILL_DIR}/references/errors.md for API details.

Wrapping: %w vs %v

  • %w wraps (callers can unwrap with errors.Is/errors.As) — default choice
  • %v creates new error with original's text only — use when underlying error is an implementation detail
  • Wrap when: caller provided the input that caused the error, or the underlying error is part of your API contract
  • Don't wrap when: error source is an implementation detail (wrapping commits you to the underlying dependency)

Wrapping Context

  • Keep context succinct: "get user: %w" not "failed to get user: %w"
  • Place %w at end of format string so error text mirrors chain structure (newest-to-oldest)
  • Don't repeat information the underlying error already provides
  • Don't annotate if annotation adds no new information — just return err

In-Band Errors

Don't use sentinel return values (-1, "", nil) to signal failure. Return (T, error) or (T, bool).

Must Functions

MustXYZ panics on error. Legitimate only for package-level initialization and test helpers. Never use in request handlers or runtime code paths.

Type Assertions

Always use comma-ok form: s, ok := val.(string). Never s := val.(string) (panics on wrong type).

Defer Errors

Don't silently ignore errors from deferred calls (f.Close(), rows.Close(), resp.Body.Close()). Propagate close error if no prior error exists. When intentionally ignoring, use _ = to make it explicit.

Internal Panic/Recover

Acceptable only when panics never escape package boundaries and a top-level deferred recover translates them to errors. Rare — see ${CLAUDE_SKILL_DIR}/references/errors.md for the full pattern.

Error Flow

Indent errors, keep happy path flat. Early return on error, never nest the happy path in else blocks.

Gotchas

Variable Shadowing

:= in inner blocks (if/for) silently hides outer variables. The err variable is commonly shadowed. Use go vet -shadow or golangci-lint to detect. Always verify assignments in inner blocks use = (not :=) when targeting outer-scope variables.

Defer Argument Evaluation

defer evaluates arguments immediately, not when the deferred function runs. Use closures to capture current values: defer func() { notify(status) }().

Defer in Loops

defer runs when the surrounding function returns, not at end of loop iteration. Extract loop body to a function so defer fires per iteration.

Slice Append Mutation

append on a slice with remaining capacity mutates the underlying array. Slices derived from the same array see each other's writes. Fix: use full slice expression s[:len(s):len(s)] to cap capacity, or explicit copy.

Strings: Runes vs Bytes

len(s) returns byte count, not rune count. Use range over string to iterate runes (not s[i]). Use utf8.RuneCountInString(s) for rune count.

String Concatenation

Use strings.Builder with Grow when concatenating in a loop — += is O(n^2). For a few fixed strings, + or fmt.Sprintf is fine.

Copy Safety

  • Never copy sync.Mutex or types containing one
  • Don't copy structs with pointer fields unless you understand aliasing
  • Copy slices/maps at API boundaries to prevent external mutation

Fixed Bit-Width Types

Prefer int unless a specific width is required by a protocol, binary format, or performance constraint. int8, uint16, etc. are prone to silent overflow.

Signal Boosting

When code does the opposite of what's common (e.g., checking err == nil instead of err != nil), add a comment to draw attention.

Typed Nil Interface Trap

A (*T)(nil) assigned to an interface is non-nil. Return explicit nil when the function returns an interface type, never a typed nil.

Concurrency

Goroutine Lifecycle

Every goroutine must have: (1) a predictable exit condition, and (2) a way for other code to wait for it to finish. No fire-and-forget goroutines — they leak memory and cause data races.

Cancellation: context.Context (Primary)

Use context.Context as the default for all goroutine lifecycle management. Caller creates context with cancel, goroutine selects on ctx.Done().

Joining: sync.WaitGroup

Use sync.WaitGroup to wait for multiple goroutines to finish. It handles joining, not cancellation. Call wg.Add(1) before launching, defer wg.Done() inside the goroutine.

Worker Lifecycle Pattern

For long-lived goroutines, wrap in a struct with context.Context for cancellation and a done channel or WaitGroup for joining. Close done channel via defer close(w.done) in the run method.

Stop + Done Channels (Alternative)

When context.Context is unavailable (infrastructure code predating context), use explicit stop/done channels. Prefer context.Context in new code.

Channels vs Mutexes

  • Parallel goroutines accessing shared statesync.Mutex (synchronization)
  • Concurrent goroutines coordinating work — channels (communication/orchestration)
  • Transferring ownership of a resource — channels (signaling completion)

Mutexes protect shared state. Channels coordinate independent actors.

Channel Rules

  • Size: zero or one. Larger buffers require justification — you must know what prevents the channel from filling.
  • Direction in signatures. Specify foo_test.go`
  • Keep related code together — don't scatter features across files
  • doc.go for package-level documentation if needed
  • Kebab-case for Go source files: user-service.go, http-handler.go

Backward-Incompatible Changes

Staged workflow: (1) add new code without touching old, (2) migrate callers, (3) remove old code. Each step is a separate commit. Never combine breaking changes with new functionality.

Configuration Patterns

For constructors with 3+ optional parameters, choose between option structs and functional options.

Option Structs

Use when most callers need several options, or options are shared across functions. Benefits: self-documenting field names, zero-value omission, easy to share and extend. Never include context.Context in option structs.

Functional Options

Use when most callers need zero options, there are many options, or options require validation. Use the interface form (type Option interface{ apply(*options) }) over closures for testability. Options should accept parameters, not use presence as signal: rpc.FailFast(true) not rpc.EnableFailFast().

Decision Criteria

| Factor | Option Struct | Functional Options | | --------------------------------- | ------------- | ------------------ | | Most callers need several options | Prefer | Either | | Most callers need zero options | Either | Prefer | | Options need validation | Either | Prefer | | Options shared across functions | Prefer | Either | | Third-party extensibility needed | Avoid | Prefer |

Zero-Value Design

Design types so the zero value is immediately useful — no constructor needed. var buf bytes.Buffer is ready to use. Only write constructors when non-zero defaults are required.

Embedding

Embedding promotes methods of the inner type to the outer type. Use embedding when promoted methods ARE your intended API. Use named fields when you don't want to expose the inner type's full method set. Never embed in public API structs unless the promoted surface is intentional — it commits your API to every exported method including future additions. Embedding in internal/ types is lower risk.

Long-Running Process Naming

  • Run — blocks until process completes. Caller controls the goroutine.
  • Start — returns immediately, spawns internal goroutine. Accept context.Context as first parameter for

cancellation.

Type Preferences

  • Prefer any over interface{} (Go 1.18+). Only use any when truly a

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.