Golang Patterns
antoniopaya22/go-rest-template
Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.
Go idioms and patterns: stdlib conventions, explicit error wrapping (%w), small interfaces, goroutines/channels concurrency, table-driven tests, and gofumpt formatting.
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install irahardianto/awesome-agv go-idioms --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/go-idioms .claude/skills/go-idioms && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .claude/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idiomsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install irahardianto/awesome-agv go-idioms --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/go-idioms .agents/skills/go-idioms && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .agents/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install irahardianto/awesome-agv go-idioms --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/go-idioms .cursor/skills/go-idioms && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .cursor/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/irahardianto/awesome-agv.git --path .agents/skills/go-idioms--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install irahardianto/awesome-agv go-idioms --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/go-idioms .gemini/skills/go-idioms && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .gemini/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install irahardianto/awesome-agv go-idiomsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/go-idioms .github/skills/go-idioms && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .github/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add irahardianto/awesome-agv --skill go-idioms -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install irahardianto/awesome-agv go-idioms --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/irahardianto/awesome-agv.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/go-idioms .opencode/skills/go-idioms && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "go-idioms" agent skill from https://github.com/irahardianto/awesome-agv/tree/main/.agents/skills/go-idioms into .opencode/skills/go-idioms/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "go-idioms", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
go-idiomsGo idioms and patterns: stdlib conventions, explicit error wrapping (%w), small interfaces, goroutines/channels concurrency, table-driven tests, and gofumpt formatting.
Go Idioms is an agent skill from irahardianto/awesome-agv. Go idioms and patterns: stdlib conventions, explicit error wrapping (%w), small interfaces, goroutines/channels concurrency, table-driven tests, and gofumpt formatting. Use when writing, refactoring, or reviewing Go services and packages.
Its SKILL.md is about 6.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files, including reference files (for example `references/go-patterns-and-anti-patterns.md`, `references/project-structure.md` and `references/recommended-dependencies.md`).
It sits in Development, covering Async programming and Refactoring. It works with Go. The repository describes itself as: Comprehensive sets of standards and practices designed to elevate the capabilities of AI coding agents. The licence is MIT.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 9e997ba. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
goFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Go Idioms loads about 6.3k tokens when it runs, and up to ~15k if it reads all its reference files. Until then it costs about 62 tokens; SKILL.md has 2,149 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from irahardianto/awesome-agv at commit 9e997ba, republished under its MIT licence (© irahardianto). 2,149 words, ~6,251 tokens.
.claude/skills/go-idioms/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.Go favors simplicity, explicitness, and readability. The language is intentionally small — resist the urge to import patterns from other languages. If it looks boring and obvious, it's probably idiomatic Go.
Scope: This file covers Go-specific coding idioms. For file layout, see
references/project-structure.md. For detailed safety, SAST, and performance patterns, seereferences/go-patterns-and-anti-patterns.md. For logging library choice, see thelogging-implementationskill. For quality commands, see thecode-idioms-and-conventionsrule.
Loading Guards: If the project has no go.mod, this skill does not apply.
| Situation | Reference to Load |
|---|---|
| Starting a new project or setting up file layout | references/project-structure.md |
Choosing packages, go.mod setup, or golangci-lint config | references/recommended-dependencies.md |
| Writing code that handles I/O, concurrency, or user input | references/go-patterns-and-anti-patterns.md |
Default to the latest stable Go release. As of August 2026, Go 1.24 is the baseline with the Go module system.
Key version milestones:
go tool for tool dependencies, swiss table map implementationrange-over-func (iterator protocol), unique packagerange-over-integer, loop variable per-iteration scoping (eliminates loop variable capture bugs), enhanced ServeMux with method-based routinglog/slog structured logging, maps and slices stdlib packages, sync.OnceFunc / sync.OnceValue / sync.OnceValues, min / max builtins, clear builtintesting.FExample go.mod configuration:
module github.com/user/project
go 1.24.0
toolchain go1.24.0Always return errors — never panic in library or business code
panic is reserved for truly unrecoverable states (programmer errors, nil dereference)recover only at top-level goroutine boundaries (middleware, server startup)Wrap errors with context using %w
✅ Preserves the error chain for errors.Is / errors.As
return fmt.Errorf("creating task for user %s: %w", userID, err)❌ Loses the error chain
return fmt.Errorf("creating task: %v", err)Error string formatting
✅ Correct formatting
return fmt.Errorf("failed to open file %q: %w", path, err)❌ Incorrect formatting
return fmt.Errorf("Failed to open file: %w.", err)Use errors.Join for multi-error aggregation (Go 1.20+)
var errs []error
if err := doFirst(); err != nil {
errs = append(errs, err)
}
if err := doSecond(); err != nil {
errs = append(errs, err)
}
return errors.Join(errs...)Use sentinel errors for expected branch conditions
// Define in errors.go
var ErrNotFound = errors.New("not found")
var ErrUnauthorized = errors.New("unauthorized")
// Caller checks with errors.Is
if errors.Is(err, ErrNotFound) {
// handle
}Use typed errors for rich domain errors
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
// Caller unwraps with errors.As
var ve *ValidationError
if errors.As(err, &ve) {
// access ve.Field, ve.Message
}Handle errors at the right level
Use lazy evaluation for fallback values
Keep interfaces small — one or two methods is ideal
✅ Focused, composable
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }❌ Monolithic
type FileManager interface {
Read()
Write()
Delete()
List()
Stat()
}"Accept interfaces, return structs"
Define interfaces where they are used, not where they are implemented
✅ Defined in the consumer package (task feature)
// task/storage.go
type Storage interface {
GetByID(ctx context.Context, id string) (*Task, error)
}
// postgres.go implements Storage — it does NOT define itImplicit satisfaction is a feature — don't use embedding to "implement" interfaces
implements keyword needed or wantedany vs interface{}
any (Go 1.18+ alias) instead of interface{}.Interface compliance verification
var _ Interface = (*Struct)(nil) to guarantee a type implements an interface at compile time.var _ Storage = (*PostgresStorage)(nil)When NOT to use interfaces
Generic interfaces with type parameters (Go 1.18+)
type Repository[T any] interface {
FindByID(ctx context.Context, id string) (T, error)
}For general concurrency principles (race conditions, deadlocks, message passing), see
concurrency-and-threading-principles.md. This section covers Go-specific mechanics.
Always pass context.Context as the first parameter
✅ Pass context
func (s *Service) GetTask(ctx context.Context, id string) (*Task, error)❌ No way to cancel or propagate deadlines
func (s *Service) GetTask(id string) (*Task, error)Never start a goroutine without knowing how it will stop (Anti-pattern: Goroutine Leak)
context.Context cancellation or channel close).✅ Goroutine is bounded by context cancellation
go func() {
for {
select {
case <-ctx.Done():
return
case item := <-ch:
process(item)
}
}
}()Goroutine lifecycle management with sync.WaitGroup
var wg sync.WaitGroup
for _, task := range tasks {
wg.Add(1)
go func(t Task) {
defer wg.Done()
process(t)
}(task)
}
wg.Wait()Use errgroup for concurrent fan-out with error collection
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return fetchUsers(ctx) })
g.Go(func() error { return fetchOrders(ctx) })
if err := g.Wait(); err != nil { ... }Prefer channels for ownership transfer; mutexes for shared state
Close channels from the sender, never the receiver
context.AfterFunc (Go 1.21+)
stop := context.AfterFunc(ctx, func() {
cleanupResources()
})
defer stop()Channel direction restrictions in function signatures
func producer(ch chan<- int) {} // Send-only
func consumer(ch <-chan int) {} // Receive-onlysync.OnceFunc / sync.OnceValue (Go 1.21+)
var initDb = sync.OnceValue(func() *sql.DB {
// initialize DB
return db
})select with default for non-blocking operations
select {
case ch <- data:
// Sent
default:
// Channel is full or unbuffered with no receiver, move on
}context.WithoutCancel (Go 1.21+)
// Request-scoped context cancelled when handler returns
bgCtx := context.WithoutCancel(ctx)
go func() {
// This work continues even after the parent request completes
auditLog(bgCtx, event)
}()⚠️ Use sparingly — most work should respect parent cancellation. Reserve for fire-and-forget operations where partial completion is acceptable.
Structured concurrency with conc (when errgroup isn't enough)
errgroup covers fan-out with error collection. For additional needs — panic recovery within goroutines, bounded worker pools with panic safety — use github.com/sourcegraph/conc.// conc.WaitGroup recovers panics and re-panics on Wait()
wg := conc.NewWaitGroup()
wg.Go(func() { process(item1) })
wg.Go(func() { process(item2) })
wg.Wait() // Re-panics if any goroutine panickederrgroup for simple fan-out. Reach for conc when you need panic recovery or bounded concurrency with conc/pool.Receiver names: short, consistent, and the first letter of the type ✅
func (s *Service) Create(...) {}❌
func (svc *Service) Create(...) {} // too verbose
func (self *Service) Create(...) {} // not GoPackage names: short, lowercase, no underscores, no plurals
✅ package task
❌ package tasks (plural)
❌ package task_service (underscore)
Acronyms follow Go conventions (all caps or all lowercase)
✅ userID, HTTPClient
❌ userId, HttpClient
Unexported identifiers omit the type name — if it's private, keep it terse
Don't stutter — task.Task is fine; task.TaskService is not
Error string formatting
Getter naming
Get prefix in Go. Use user.Name() not user.GetName().Test naming
TestFunctionName_Scenario_Expected pattern.func TestCalculateDiscount_NegativeInput_ReturnsError(t *testing.T) { ... }Constructor naming
New prefix (NewService, NewClient).Interface naming
-er suffix for single-method interfaces (Reader, Writer, Stringer).Range-over-func iterators (Go 1.23+)
// Definition
func Count(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := 0; i < n; i++ {
if !yield(i) {
return
}
}
}
}
// Usage
for i := range Count(5) {
fmt.Println(i)
}Nil slice vs empty slice semantics
var s []int (nil slice) marshals to null in JSON.s := []int{} or make([]int, 0) (empty slice) marshals to [] in JSON.Type assertions with comma-ok pattern
if str, ok := val.(string); ok {
fmt.Println(str)
}Type switches for polymorphic dispatch
switch v := val.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Int:", v)
default:
fmt.Println("Unknown type")
}strings.Builder for string concatenation
+ inside loops. Use strings.Builder.var b strings.Builder
b.WriteString("hello")
b.WriteString("world")
return b.String()slices and maps stdlib packages (Go 1.21+)
slices.Sort(mySlice)
if slices.Contains(mySlice, target) { ... }Variadic append
append(s1, s2...) instead of loops.Pre-allocation
make([]T, 0, n) when the size is known to avoid reallocation overhead.sync.Pool for hot-path allocations
time.NewTicker vs time.Tick
time.Tick leaks the underlying ticker. Always prefer time.NewTicker which can be stopped via ticker.Stop().Struct embedding for composition (not inheritance)
Early returns / guard clauses
if err != nil {
return err
}
// Continue happy pathKeep function complexity low
Functional options for optional configuration
type Option func(*Service)
func WithTimeout(d time.Duration) Option {
return func(s *Service) { s.timeout = d }
}
func NewService(store Storage, opts ...Option) *Service {
s := &Service{store: store, timeout: 30 * time.Second}
for _, o := range opts { o(s) }
return s
}defer for cleanup — always use error-checked closures Every deferred cleanup call that returns an error MUST check and log the error.
Never use bare defer X.Close() — the discarded error hides resource leak failures.
❌ NEVER: Error silently discarded
defer rows.Close()✅ ALWAYS: Error-checked closure with structured logging
rows, err := db.QueryContext(ctx, query)
if err != nil { return fmt.Errorf("querying tasks: %w", err) }
defer func() {
if err := rows.Close(); err != nil {
slog.Warn("failed to close rows", "error", err, "operation", "ListTasks")
}
}()Transaction rollback: ❌ NEVER
defer tx.Rollback()✅ ALWAYS: Guard against sql.ErrTxDone (already committed)
defer func() {
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
slog.Error("failed to rollback transaction", "error", err, "operation", "CreateOrder")
}
}()HTTP response body: ❌ NEVER
defer resp.Body.Close()✅ ALWAYS: Drain then close (prevents connection reuse issues)
defer func() {
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
slog.Warn("failed to drain response body", "error", err)
}
if err := resp.Body.Close(); err != nil {
slog.Warn("failed to close response body", "error", err)
}
}()Avoid init() functions
main or constructors.Use named return values only for documentation or defer-based cleanup
unique.Handle for string interning (Go 1.23+)
import "unique"
h := unique.Make("active")
// h.Value() returns "active"
// Two handles with the same underlying value compare as equal
h1 := unique.Make("active")
fmt.Println(h == h1) // true — same canonical representationTest file naming and pyramid proportions are defined in
testing-strategy.md. This section covers Go-specific tooling only.
Table-driven tests are the default pattern
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
input float64
expected float64
wantErr bool
}{
{"zero items", 0, 0, false},
{"negative input", -1, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := calculateDiscount(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.expected, got)
})
}
}Use testify for assertions
require for fatal assertions, assert for non-fatal.Run with the race detector in CI
go test -race ./...Use httptest.NewRecorder() for HTTP handler tests
Test behaviour, not implementation
Test helpers with t.Helper() and testing.TB interface
func createTestUser(t testing.TB) *User {
t.Helper() // Corrects line numbers in failure output
// ...
}t.Cleanup() for resource teardown
func setupDB(t *testing.T) *sql.DB {
db := connect()
t.Cleanup(func() { db.Close() })
return db
}Fuzz testing with testing.F (Go 1.18+)
func FuzzParse(f *testing.F) {
f.Add("valid_input")
f.Fuzz(func(t *testing.T, input string) {
Parse(input) // ensure it never panics
})
}Benchmark patterns with testing.B
func BenchmarkProcess(b *testing.B) {
b.ReportAllocs() // ALWAYS report allocations
b.ResetTimer() // Ignore setup time
for i := 0; i < b.N; i++ {
Process()
}
}Golden file testing
Test coverage non-negotiable policy
Coverage commands
go test -cover ./... and go test -coverprofile=coverage.out ./....Test Double Selection Table
| Approach | When to Use |
|---|---|
| Hand-written fake (implement interface) | Simple interface, few methods, need stateful behavior |
gomock/mockgen | Complex interface, need to verify call counts, argument matching |
testify/mock | Complex interface, prefer fluent assertion API |
| Table-driven tests | Same logic, multiple input/output pairs |
go-cmp | Deep equality comparison with custom options |
Prefer hand-written fakes for core domain traits.
All of the following must pass with zero warnings/errors before any commit. See code-idioms-and-conventions.md for the full checklist.
Feedback Loop — Development Workflow:
| Phase | Command | Purpose |
|---|---|---|
| TDD / rapid iteration | go vet ./... | Type-checks and correctness — fastest loop |
| Pre-commit | golangci-lint run | Aggregated linting — must pass zero warnings |
| Pre-commit | gofumpt -l -w . | Formatting — non-negotiable |
| Pre-commit | go test -race ./... | Unit tests with race detector |
| Coverage verification | go test -coverprofile=c.out ./... | Verify before merging |
| Security audit | govulncheck ./... | CVE scanning |
Lint suppression policy — NEVER suppress these:
| Lint | What It Signals | What To Do Instead |
|---|---|---|
errcheck | Unchecked error returns | Handle the error — use error-checked closure in defer |
govet shadow | Variable shadowing | Rename the inner variable |
staticcheck SA* | Correctness issues | Fix the underlying bug |
gosec G* (security) | Security vulnerability | Fix the vulnerability |
cyclop / gocognit | Function too complex | Decompose into smaller functions |
Acceptable suppressions (with mandatory // nolint: comment + rationale):
| Lint | When Acceptable |
|---|---|
gosec G104 (unhandled error) | In test code only, with // nolint:gosec // test-only |
revive exported | When the exported symbol is intentionally part of API design |
Rule of thumb: If you're about to write //nolint:, stop and ask if you're suppressing a real design problem.
//nolint:errcheck is NEVER acceptable. If a function returns an error, handle it — even in defer. Use an error-checked closure. This is the #1 source of audit findings.
Logging: Never use
fmt.Printlnorlog.Printfin production service code — these produce unstructured output. Uselog/slog(stdlib, Go 1.21+) or the project's chosen adapter. See@.agents/skills/logging-implementation/SKILL.mdfor the required library and patterns.
Godoc conventions
Example functions
func ExampleFunction() with // Output: comments at the end of the function body for runnable examples in Godoc.func ExampleHello() {
fmt.Println("Hello")
// Output: Hello
}Package doc
doc.go file.Document the WHY, not the WHAT
Minimize dependency count
Commit go.sum always
go.mod and go.sum belong in version control to guarantee reproducible builds.go mod tidy before commits
govulncheck in CI
Module proxy and checksum database
GOPROXY and the checksum database. For private modules, configure GOPRIVATE or GONOSUMCHECK.Prefer stdlib over third-party
log/slog vs zap), use the stdlib.Never scatter os.Getenv() calls throughout codebase
Centralized config struct parsed at startup
Config struct once, then inject it (or its subsets) where needed.Fail fast
Example with struct tags and validation
type Config struct {
Port int `env:"PORT" envDefault:"8080"`
Database string `env:"DATABASE_URL" envRequired:"true"`
}
func LoadConfig() (*Config, error) {
// load and validate
}defer.references/go-patterns-and-anti-patterns.md.perf-optimization skill.© irahardianto, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 3 other files (references) in .agents/skills/go-idioms of irahardianto/awesome-agv.
Open the folder on GitHubat commit 9e997ba
Go Idioms next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Go Idioms this skillirahardianto/awesome-agv | 156 | — | ~6.3k | Automated safety check: Pass | MIT | |
| Golang Patternsantoniopaya22/go-rest-template | 172 | 9 repos | ~3.5k | Automated safety check: Pass | None | |
| GolangJanDeDobbeleer/oh-my-posh | 24k | — | ~705 | Automated safety check: Pass | MIT | |
| Valgocohesivestack/valgo | 508 | — | ~2.4k | Automated safety check: Pass | MIT | |
| Context Handling in fp-goIBM/fp-go | 2k | — | ~4.2k | Automated safety check: Pass | Apache-2.0 | |
| Typed Dependencies with fp-go EffectIBM/fp-go | 2k | — | ~4.3k | Automated safety check: Pass | Apache-2.0 |
antoniopaya22/go-rest-template
Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.
JanDeDobbeleer/oh-my-posh
Go conventions for this project that differ from common Go practice.
cohesivestack/valgo
Add, refactor, debug, review, explain, or migrate type-safe validation in consumer Go applications using github.com/cohesivestack/valgo.
IBM/fp-go
Covers handling Go's context.Context idiomatically in fp-go code: reading and scoping context through operators, timeouts, cancellation and converting ctx-first functions.
IBM/fp-go
Teaches an agent to write fp-go v2 services with the Effect type, carrying dependencies in its type parameter instead of in context.Context or parameters.
ozgurcd/gograph
Gives an agent working in a Go codebase a structural view through a local MCP server: call graphs, blast-radius and impact analysis, and bounded first-call exploration.
irahardianto/awesome-agv
Commits to one bold aesthetic direction, sets up a CSS token system for it, then builds the interface in Vue or plain HTML using those tokens.
irahardianto/awesome-agv
Profile-driven performance optimization protocol. An agent skill from irahardianto/awesome-agv.
irahardianto/awesome-agv
Coding conventions for Angular 19 and later: standalone components, signals, OnPush change detection, lazy routes and where RxJS still belongs.
irahardianto/awesome-agv
Rules for designing CI/CD pipelines in layers: universal lint, test and scan stages, container builds with SBOM attestation, and GitOps for orchestrated deployments.
irahardianto/awesome-agv
Hono lightweight web framework patterns: type-safe route handlers, middleware composition, Zod validation, and RPC clients for Cloudflare Workers, Node, or Bun.
irahardianto/awesome-agv
Mobile E2E testing patterns — Flutter integrationtest, Patrol, Maestro, golden testing, device matrix, and test data management.
Works with
Categories
Go idioms and patterns: stdlib conventions, explicit error wrapping (%w), small interfaces, goroutines/channels concurrency, table-driven tests, and gofumpt formatting. Go Idioms is an agent skill from irahardianto/awesome-agv. Go idioms and patterns: stdlib conventions, explicit error wrapping (%w), small interfaces, goroutines/channels concurrency, table-driven tests, and gofumpt formatting.
Go Idioms fits situations like: reviewing Go services and packages; tasks that involve Async programming; tasks that involve Refactoring.
Run `npx skills add irahardianto/awesome-agv --skill go-idioms -a claude-code`. Or copy the skill folder (.agents/skills/go-idioms in irahardianto/awesome-agv) into .claude/skills/go-idioms in your project. Claude Code loads it when a task matches its description.
Run `npx skills add irahardianto/awesome-agv --skill go-idioms -a codex`. Or copy the skill folder (.agents/skills/go-idioms in irahardianto/awesome-agv) into .agents/skills/go-idioms in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add irahardianto/awesome-agv --skill go-idioms -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/go-idioms, .gemini/skills/go-idioms, .github/skills/go-idioms and .opencode/skills/go-idioms in your project.
Going by SKILL.md and its folder, Go Idioms needs the command-line tools its instructions call (go).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Go Idioms is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.3k tokens (SKILL.md is roughly 25k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 9k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Go Idioms: Golang Patterns (antoniopaya22/go-rest-template, 172 stars), Golang (JanDeDobbeleer/oh-my-posh, 24k stars), Valgo (cohesivestack/valgo, 508 stars) and Context Handling in fp-go (IBM/fp-go, 2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
irahardianto (a GitHub user) maintains it in irahardianto/awesome-agv, which has 156 GitHub stars. The repository holds 34 skills in this directory. The repository was last updated on October 5, 2026.
Source: irahardianto/awesome-agv on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.