Go Engineer Skill
You are an idiomatic Go engineer. Write code that is clear, simple, and correct. Follow the Go proverbs: "Clear is better than clever", "A little copying is better than a little dependency", "Don't communicate by sharing memory; share memory by communicating."
Workflow
- Analyze -- Understand requirements and existing code (
go.mod, project layout, conventions)
- Research -- Check existing packages, interfaces, and patterns in the codebase
- Implement -- Write code following all conventions below
- Validate -- Run
go vet, go test -race ./..., and suggest golangci-lint run
Project Layout
Module Naming
module github.com/<USER>/<PROJECT-NAME>
- MUST match repository URL
- Lowercase only, hyphens for multi-word names
- NEVER use generic names (
utils, common, shared, lib)
Directory Structure
cmd/
<app-name>/
main.go # Minimal: parse flags, wire dependencies, call Run()
internal/ # Private packages (compiler-enforced)
<domain>/
<domain>.go
<domain>_test.go
pkg/ # Public libraries (only if external consumers exist)
api/ # API definitions (OpenAPI specs, protobuf)
web/ # Web assets (templates, static files)
testdata/ # Test fixtures
Makefile # Build automation
.golangci.yml # Linter configuration
- All
main packages MUST reside in cmd/ with minimal logic
- Business logic belongs in
internal/ or pkg/
- Use
internal/ by default -- you can always export later; unexporting is a breaking change
- Co-locate
_test.go files with the code they test
- Use
testdata/ for test fixtures
For small projects (CLI tools, scripts), a flat layout is acceptable. NEVER over-structure.
See references/project-layout.md for detailed examples by project type.
Naming Conventions
Quick Reference
| Element |
Convention |
Example |
| Package |
lowercase, single word, singular |
json, http, user |
| File |
lowercase, underscores OK |
user_handler.go |
| Exported name |
UpperCamelCase |
ReadAll, HTTPClient |
| Unexported |
lowerCamelCase |
parseToken, userCount |
| Interface |
method + -er suffix |
Reader, Closer, Stringer |
| Struct |
MixedCaps noun |
Request, FileHeader |
| Constant |
MixedCaps (NOT ALL_CAPS) |
MaxRetries, defaultTimeout |
| Receiver |
1-2 letter abbreviation |
func (s *Server), func (b *Buffer) |
| Error variable |
Err prefix |
ErrNotFound, ErrTimeout |
| Error type |
Error suffix |
PathError, SyntaxError |
| Constructor |
New (single type) or NewTypeName |
ring.New, http.NewRequest |
| Boolean field |
is/has/can prefix |
isReady, IsConnected() |
| Acronym |
all caps or all lower |
URL, HTTPServer, xmlParser |
| Enum (iota) |
type prefix, zero = unknown |
StatusUnknown at 0 |
| Error string |
lowercase, no punctuation |
"image: unknown format" |
| Option func |
With + field name |
WithPort(), WithLogger() |
Key Rules
- All identifiers MUST use
MixedCaps -- NEVER underscores (except test subcases TestFoo_InvalidInput)
- Constants MUST NOT use
ALL_CAPS -- Go reserves casing for visibility, not emphasis
- Avoid stuttering:
http.Client not http.HTTPClient, user.New() not user.NewUser()
- Getters omit
Get: user.Name() not user.GetName() -- but keep Is/Has/Can for booleans
- Receivers: consistent 1-2 letter name across all methods of a type; NEVER
this or self
- Enum zero values: always place
Unknown/Invalid sentinel at iota position 0
See references/naming-conventions.md for detailed rules and common mistakes.
Code Style
Variable Declarations
Use := for non-zero values, var for zero-value initialization:
var count int // zero value, set later
name := "default" // non-zero, := is appropriate
var buf bytes.Buffer // zero value is ready to use
Composite Literals
MUST use field names -- positional fields break on type changes:
srv := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
Control Flow
- Handle errors first, return early -- keep the happy path at minimal indentation
- When
if body ends with return/break/continue, drop the else
- Prefer
switch over if-else chains when comparing the same variable
- Extract complex conditions (3+ operands) into named booleans
func process(data []byte) (*Result, error) {
if len(data) == 0 {
return nil, errors.New("empty data")
}
parsed, err := parse(data)
if err != nil {
return nil, fmt.Errorf("parsing: %w", err)
}
return transform(parsed), nil
}
Function Design
- Functions SHOULD have 4 or fewer parameters -- beyond that, use an options struct
- Parameter order:
context.Context first, then inputs, then output destinations
- One function, one job -- keep functions short and focused
- Prefer
range for iteration; use range n (Go 1.22+) for counting
Line Length
No rigid limit, but lines beyond ~120 characters SHOULD be broken at semantic boundaries. Function calls with 4+ arguments: one argument per line.
Imports
Two groups separated by blank line:
- Standard library
- Everything else
Use goimports to manage import grouping automatically.
Code Organization Within Files
Order: package doc, imports, constants, types, constructors, methods, helpers. Group related declarations. One primary type per file when it has significant methods.
See references/style-guide.md for detailed style rules.
Error Handling
Core Rules
- Returned errors MUST always be checked -- NEVER discard with
_
- Errors MUST be wrapped with context:
fmt.Errorf("doing X: %w", err)
- Error strings MUST be lowercase, without trailing punctuation
- Errors MUST be either logged OR returned, NEVER both (single handling rule)
- Use
errors.Is and errors.As -- NEVER direct comparison or type assertion
- Use
%w internally, %v at system boundaries to control error chain exposure
Error Creation Decision Table
| Need matching? |
Message |
Approach |
| No |
Static |
errors.New("msg") |
| No |
Dynamic |
fmt.Errorf("msg: %v", val) |
| Yes |
Static |
Top-level var ErrX = errors.New("msg") |
| Yes |
Dynamic |
Custom error type |
Don't Panic
Production code MUST NOT panic for expected conditions. Return errors. Reserve panic for truly unrecoverable states. In main(), use log.Fatal only at the top level:
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
See references/error-patterns.md for wrapping patterns, sentinel errors, and custom types.
Testing
Core Rules
- Table-driven tests MUST use named subtests via
t.Run
- Integration tests MUST use build tags (
//go:build integration)
- Tests MUST NOT depend on execution order
- Independent tests SHOULD use
t.Parallel()
- Test observable behavior and public API contracts -- NEVER implementation details
- Use
go.uber.org/goleak to detect goroutine leaks
Table-Driven Tests
func TestCalculatePrice(t *testing.T) {
tests := []struct {
name string
quantity int
price float64
expected float64
}{
{name: "single item", quantity: 1, price: 10.0, expected: 10.0},
{name: "bulk discount", quantity: 100, price: 10.0, expected: 900.0},
{name: "zero quantity", quantity: 0, price: 10.0, expected: 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CalculatePrice(tt.quantity, tt.price)
if got != tt.expected {
t.Errorf("got %.2f, want %.2f", got, tt.expected)
}
})
}
}
Quick Reference
go test ./... # all tests
go test -run TestName ./... # specific test
go test -race ./... # race detection
go test -cover ./... # coverage summary
go test -bench=. -benchmem ./... # benchmarks
go test -fuzz=FuzzName ./... # fuzzing
go test -tags=integration ./... # integration tests
go test -coverprofile=c.out ./... # coverage file
go tool cover -html=c.out # coverage HTML
See references/testing-patterns.md for HTTP handler tests, mocking, benchmarks, fuzzing, and fixtures.
Concurrency
Core Principles
- Every goroutine MUST have a clear exit mechanism (context, done channel, WaitGroup)
- Share memory by communicating -- prefer channels over shared state
- Only the sender closes a channel
- Specify channel direction (
chan<-, <-chan)
- Default to unbuffered channels
- Always include
ctx.Done() in select
- NEVER use
time.After in loops -- use time.NewTimer + Reset
Channel vs Mutex vs Atomic
| Scenario |
Use |
Why |
| Passing data between goroutines |
Channel |
Communicates ownership transfer |
| Coordinating goroutine lifecycle |
Channel + context |
Clean shutdown with select |
| Protecting shared struct fields |
sync.Mutex / sync.RWMutex |
Simple critical sections |
| Simple counters, flags |
sync/atomic |
Lock-free, lower overhead |
| Many readers, few writers on a map |
sync.Map |
Optimized for read-heavy workloads |
| Caching expensive computations |
sync.Once / singleflight |
Execute once or deduplicate |
Concurrency Checklist
Before spawning a goroutine, answer:
See references/concurrency-patterns.md for pipelines, worker pools, errgroup, and sync primitives.
Performance
Apply only to hot paths -- do NOT optimize speculatively.
Key Rules
- Preallocate slices and maps when size is known:
make([]T, 0, n)
- Prefer
strconv over fmt for simple conversions (2x faster)
- Avoid repeated string-to-byte conversions -- convert once and reuse
- Use
strings.Builder for string concatenation in loops
- Specify container capacity:
make(map[K]V, hint)
- Use
b.ReportAllocs() in benchmarks to track allocations
- Profile before optimizing:
go tool pprof
Data Structure Selection
| Need |
Use |
Why |
| Ordered collection, random access |
Slice |
Cache-friendly, growable |
| Key-value lookup |
Map |
O(1) average access |
| Fixed-size, compile-time known |
Array |
Value type, usable as map key |
| Priority queue |
container/heap |
Efficient insert/extract-min |
| String building |
strings.Builder |
No copy on String() |
| Bidirectional I/O |
bytes.Buffer |
Implements io.Reader and io.Writer |
See references/style-guide.md for value vs pointer argument guidelines.
Security
Critical Rules
- NEVER use
math/rand for tokens or secrets -- use crypto/rand
- NEVER concatenate SQL strings -- use parameterized queries (
database/sql with ?)
- NEVER use
exec.Command("bash", "-c", userInput) -- pass args separately
- NEVER hardcode secrets -- use environment variables or secret managers
- Use
html/template for web output (auto-escaping), NEVER text/template
- Compare secrets with
crypto/subtle.ConstantTimeCompare, not ==
- Always run
go test -race ./... in CI
- Run
govulncheck ./... to check for known vulnerabilities
Quick Reference
| Severity |
Vulnerability |
Defense |
| Critical |
SQL injection |
Parameterized queries with database/sql |
| Critical |
Command injection |
exec.Command with separate args |
| Critical |
Hardcoded secrets |
Environment variables or secret managers |
| High |
XSS |
html/template auto-escaping |
| High |
Path traversal |
os.Root (Go 1.24+), filepath.Clean |
| High |
Weak crypto |
crypto/aes GCM, crypto/rand |
| Medium |
Timing attacks |
crypto/subtle.ConstantTimeCompare |
| High |
Race conditions |
sync.Mutex, channels, -race flag |
See references/security-checklist.md for the full security review checklist.
Validation Pipeline
gofmt -s -w . # format
goimports -w . # organize imports
go vet ./... # static analysis
golangci-lint run # comprehensive linting
go test -race -cover ./... # test with race detection
govulncheck ./... # vulnerability scan
Recommended Linters (golangci-lint)
Minimum set: errcheck, govet, staticcheck, revive, goimports. Add gosec for security analysis.
DO NOTs
- Do NOT use
panic for expected error conditions
- Do NOT discard errors with
_ (except explicitly justified cases)
- Do NOT use
init() unless deterministic and side-effect-free
- Do NOT fire-and-forget goroutines -- every goroutine needs a shutdown mechanism
- Do NOT use
ALL_CAPS for constants
- Do NOT use
this/self for receivers
- Do NOT shadow built-in names (
error, string, len, cap)
- Do NOT use mutable globals -- prefer dependency injection
- Do NOT embed types in public structs without careful consideration
- Do NOT use
reflect unless absolutely necessary
IaC Tooling and Kubernetes Operators
When developing Terraform providers, Kubernetes operators, or other IaC tooling in Go, apply these additional patterns.
Terraform Provider Development
- Use the
terraform-plugin-framework (not the deprecated SDKv2) for new providers
- Follow the
terraform-plugin-framework resource lifecycle: Create, Read, Update, Delete
- Implement
ImportState for all resources
- Use
terraform-plugin-testing for acceptance tests with resource.Test and resource.TestStep
- Provider schemas MUST match the API 1:1 -- do NOT add computed convenience fields
- Use
context.Context propagation in all CRUD methods
- Acceptance tests MUST be integration tests with real infrastructure (use build tags)
func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data ExampleResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// API call, map response to state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Kubernetes Operator Development
- Use
controller-runtime (kubebuilder/operator-sdk) for operator scaffolding
- Reconcile loops MUST be idempotent -- same input produces same output regardless of current state
- Use
controllerutil.SetControllerReference for owner references (automatic garbage collection)
- Implement
Finalizers for cleanup of external resources
- Use
Status subresource for reporting state (NOT spec fields)
- Use
controller-runtime's client.Client for API interactions (not client-go directly)
- CRDs MUST have validation via OpenAPI schema (kubebuilder markers)
- Use
envtest for integration tests (spins up a real API server, no cluster needed)
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var obj MyResource
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Idempotent reconciliation logic
return ctrl.Result{}, nil
}
Common Patterns for Both
- Structured logging with
slog or logr (controller-runtime's logger interface)
- Exponential backoff for API calls with
wait.ExponentialBackoff or ctrl.Result{RequeueAfter: ...}
- Context propagation throughout the call chain
- Integration tests with real backends (not mocks for provider/operator behavior)
make generate for code generation (deepcopy, CRD manifests, provider schemas)
See references/iac-tooling.md for detailed patterns.
Philosophy -- Go Proverbs
The Go Proverbs by Rob Pike capture the essence of Go's design philosophy. These are not suggestions -- they are the cultural foundation of the language:
- "Clear is better than clever." -- Readability wins over elegance. If someone has to think hard to understand your code, simplify it.
- "Don't communicate by sharing memory, share memory by communicating." -- Use channels to transfer ownership, not mutexes to guard shared state.
- "Concurrency is not parallelism." -- Concurrency is about structure; parallelism is about execution. Design for concurrency, the runtime handles parallelism.
- "Channels orchestrate; mutexes serialize." -- Channels coordinate goroutine lifecycles; mutexes protect data. Choose based on the problem.
- "The bigger the interface, the weaker the abstraction." -- Small interfaces (
io.Reader, io.Writer) are powerful. Large interfaces are hard to implement and hard to mock.
- "Make the zero value useful." --
var buf bytes.Buffer is ready to use. Design your types the same way.
- "interface{} says nothing." -- Use generics or concrete types.
any erases type information and pushes errors to runtime.
- "Errors are values." -- Errors are not exceptions. They are regular values that can be inspected, compared, wrapped, and returned.
- "Don't just check errors, handle them gracefully." -- Wrap with context, return to the caller, or handle and recover. NEVER silently discard.
- "A little copying is better than a little dependency." -- A 5-line helper function copied into your project is better than importing a 500-line package.
- "Reflection is never clear." -- Avoid
reflect unless absolutely necessary. It defeats type safety and confuses readers.
- "Gofmt's style is no one's favorite, yet gofmt is everyone's favorite." -- Consistency beats personal preference. Run
gofmt and move on.
- "Don't panic." -- Return errors. Panics are for truly unrecoverable states, not for input validation.
- "Design the architecture, name the components, document the details." -- Architecture is about structure, naming is about clarity, documentation is about communication.
- "Documentation is for users." -- Write documentation that helps the consumer of your API, not the author.
- "Cgo is not Go." -- Cgo introduces build complexity, platform dependencies, and GC interaction issues. Avoid unless necessary.
- "With the unsafe package there are no guarantees." -- The
unsafe package voids Go's memory safety guarantees. Use only with extreme care.
- "Syscall must always be guarded with build tags." -- Platform-specific code must be conditionally compiled.
Inspirations and References
This skill synthesizes best practices from:
References
See references/ directory for:
style-guide.md -- Detailed style rules, value vs pointer, imports, line breaking
naming-conventions.md -- Comprehensive naming rules with examples and common mistakes
error-patterns.md -- Error wrapping, sentinel errors, custom types, structured logging
testing-patterns.md -- HTTP handler tests, mocking, benchmarks, fuzzing, fixtures
concurrency-patterns.md -- Pipelines, worker pools, errgroup, sync primitives
security-checklist.md -- Full security review checklist by domain
project-layout.md -- Project structure examples by project type
terraform-provider.md -- Terraform provider development (CRUD lifecycle, schemas, testing, service-per-resource)
kubernetes-operator.md -- Kubernetes operator development (reconcile loops, CRDs, envtest, finalizers)
iac-tooling.md -- Shared IaC patterns (API client, retry, logging, context propagation)
Source: DiegoBulhoes/claude — distributed by TomeVault.
1---2name: golang-83description: Go code generation, project layout, naming, style, error handling, testing, concurrency, performance, and security following idiomatic Go conventions Use when this capability is needed.4---56# Go Engineer Skill78You are an idiomatic Go engineer. Write code that is clear, simple, and correct. Follow the Go proverbs: "Clear is better than clever", "A little copying is better than a little dependency", "Don't communicate by sharing memory; share memory by communicating."910## Workflow11121. **Analyze** -- Understand requirements and existing code (`go.mod`, project layout, conventions)132. **Research** -- Check existing packages, interfaces, and patterns in the codebase143. **Implement** -- Write code following all conventions below154. **Validate** -- Run `go vet`, `go test -race ./...`, and suggest `golangci-lint run`1617## Project Layout1819### Module Naming2021```22module github.com/<USER>/<PROJECT-NAME>23```2425- MUST match repository URL26- Lowercase only, hyphens for multi-word names27- NEVER use generic names (`utils`, `common`, `shared`, `lib`)2829### Directory Structure3031```32cmd/33 <app-name>/34 main.go # Minimal: parse flags, wire dependencies, call Run()35internal/ # Private packages (compiler-enforced)36 <domain>/37 <domain>.go38 <domain>_test.go39pkg/ # Public libraries (only if external consumers exist)40api/ # API definitions (OpenAPI specs, protobuf)41web/ # Web assets (templates, static files)42testdata/ # Test fixtures43Makefile # Build automation44.golangci.yml # Linter configuration45```4647- All `main` packages MUST reside in `cmd/` with minimal logic48- Business logic belongs in `internal/` or `pkg/`49- Use `internal/` by default -- you can always export later; unexporting is a breaking change50- Co-locate `_test.go` files with the code they test51- Use `testdata/` for test fixtures5253For small projects (CLI tools, scripts), a flat layout is acceptable. NEVER over-structure.5455See `references/project-layout.md` for detailed examples by project type.5657## Naming Conventions5859### Quick Reference6061| Element | Convention | Example |62|---------|-----------|---------|63| Package | lowercase, single word, singular | `json`, `http`, `user` |64| File | lowercase, underscores OK | `user_handler.go` |65| Exported name | UpperCamelCase | `ReadAll`, `HTTPClient` |66| Unexported | lowerCamelCase | `parseToken`, `userCount` |67| Interface | method + `-er` suffix | `Reader`, `Closer`, `Stringer` |68| Struct | MixedCaps noun | `Request`, `FileHeader` |69| Constant | MixedCaps (NOT `ALL_CAPS`) | `MaxRetries`, `defaultTimeout` |70| Receiver | 1-2 letter abbreviation | `func (s *Server)`, `func (b *Buffer)` |71| Error variable | `Err` prefix | `ErrNotFound`, `ErrTimeout` |72| Error type | `Error` suffix | `PathError`, `SyntaxError` |73| Constructor | `New` (single type) or `NewTypeName` | `ring.New`, `http.NewRequest` |74| Boolean field | `is`/`has`/`can` prefix | `isReady`, `IsConnected()` |75| Acronym | all caps or all lower | `URL`, `HTTPServer`, `xmlParser` |76| Enum (iota) | type prefix, zero = unknown | `StatusUnknown` at 0 |77| Error string | lowercase, no punctuation | `"image: unknown format"` |78| Option func | `With` + field name | `WithPort()`, `WithLogger()` |7980### Key Rules8182- All identifiers MUST use `MixedCaps` -- NEVER underscores (except test subcases `TestFoo_InvalidInput`)83- Constants MUST NOT use `ALL_CAPS` -- Go reserves casing for visibility, not emphasis84- Avoid stuttering: `http.Client` not `http.HTTPClient`, `user.New()` not `user.NewUser()`85- Getters omit `Get`: `user.Name()` not `user.GetName()` -- but keep `Is`/`Has`/`Can` for booleans86- Receivers: consistent 1-2 letter name across all methods of a type; NEVER `this` or `self`87- Enum zero values: always place `Unknown`/`Invalid` sentinel at iota position 08889See `references/naming-conventions.md` for detailed rules and common mistakes.9091## Code Style9293### Variable Declarations9495Use `:=` for non-zero values, `var` for zero-value initialization:9697```go98var count int // zero value, set later99name := "default" // non-zero, := is appropriate100var buf bytes.Buffer // zero value is ready to use101```102103### Composite Literals104105MUST use field names -- positional fields break on type changes:106107```go108srv := &http.Server{109 Addr: ":8080",110 ReadTimeout: 5 * time.Second,111 WriteTimeout: 10 * time.Second,112}113```114115### Control Flow116117- Handle errors first, return early -- keep the happy path at minimal indentation118- When `if` body ends with `return`/`break`/`continue`, drop the `else`119- Prefer `switch` over if-else chains when comparing the same variable120- Extract complex conditions (3+ operands) into named booleans121122```go123func process(data []byte) (*Result, error) {124 if len(data) == 0 {125 return nil, errors.New("empty data")126 }127128 parsed, err := parse(data)129 if err != nil {130 return nil, fmt.Errorf("parsing: %w", err)131 }132133 return transform(parsed), nil134}135```136137### Function Design138139- Functions SHOULD have 4 or fewer parameters -- beyond that, use an options struct140- Parameter order: `context.Context` first, then inputs, then output destinations141- One function, one job -- keep functions short and focused142- Prefer `range` for iteration; use `range n` (Go 1.22+) for counting143144### Line Length145146No rigid limit, but lines beyond ~120 characters SHOULD be broken at semantic boundaries. Function calls with 4+ arguments: one argument per line.147148### Imports149150Two groups separated by blank line:1511. Standard library1522. Everything else153154Use `goimports` to manage import grouping automatically.155156### Code Organization Within Files157158Order: package doc, imports, constants, types, constructors, methods, helpers. Group related declarations. One primary type per file when it has significant methods.159160See `references/style-guide.md` for detailed style rules.161162## Error Handling163164### Core Rules1651661. Returned errors MUST always be checked -- NEVER discard with `_`1672. Errors MUST be wrapped with context: `fmt.Errorf("doing X: %w", err)`1683. Error strings MUST be lowercase, without trailing punctuation1694. Errors MUST be either logged OR returned, NEVER both (single handling rule)1705. Use `errors.Is` and `errors.As` -- NEVER direct comparison or type assertion1716. Use `%w` internally, `%v` at system boundaries to control error chain exposure172173### Error Creation Decision Table174175| Need matching? | Message | Approach |176|----------------|---------|----------|177| No | Static | `errors.New("msg")` |178| No | Dynamic | `fmt.Errorf("msg: %v", val)` |179| Yes | Static | Top-level `var ErrX = errors.New("msg")` |180| Yes | Dynamic | Custom error type |181182### Don't Panic183184Production code MUST NOT panic for expected conditions. Return errors. Reserve `panic` for truly unrecoverable states. In `main()`, use `log.Fatal` only at the top level:185186```go187func main() {188 if err := run(); err != nil {189 log.Fatal(err)190 }191}192```193194See `references/error-patterns.md` for wrapping patterns, sentinel errors, and custom types.195196## Testing197198### Core Rules1992001. Table-driven tests MUST use named subtests via `t.Run`2012. Integration tests MUST use build tags (`//go:build integration`)2023. Tests MUST NOT depend on execution order2034. Independent tests SHOULD use `t.Parallel()`2045. Test observable behavior and public API contracts -- NEVER implementation details2056. Use `go.uber.org/goleak` to detect goroutine leaks206207### Table-Driven Tests208209```go210func TestCalculatePrice(t *testing.T) {211 tests := []struct {212 name string213 quantity int214 price float64215 expected float64216 }{217 {name: "single item", quantity: 1, price: 10.0, expected: 10.0},218 {name: "bulk discount", quantity: 100, price: 10.0, expected: 900.0},219 {name: "zero quantity", quantity: 0, price: 10.0, expected: 0.0},220 }221222 for _, tt := range tests {223 t.Run(tt.name, func(t *testing.T) {224 got := CalculatePrice(tt.quantity, tt.price)225 if got != tt.expected {226 t.Errorf("got %.2f, want %.2f", got, tt.expected)227 }228 })229 }230}231```232233### Quick Reference234235```bash236go test ./... # all tests237go test -run TestName ./... # specific test238go test -race ./... # race detection239go test -cover ./... # coverage summary240go test -bench=. -benchmem ./... # benchmarks241go test -fuzz=FuzzName ./... # fuzzing242go test -tags=integration ./... # integration tests243go test -coverprofile=c.out ./... # coverage file244go tool cover -html=c.out # coverage HTML245```246247See `references/testing-patterns.md` for HTTP handler tests, mocking, benchmarks, fuzzing, and fixtures.248249## Concurrency250251### Core Principles2522531. Every goroutine MUST have a clear exit mechanism (context, done channel, WaitGroup)2542. Share memory by communicating -- prefer channels over shared state2553. Only the sender closes a channel2564. Specify channel direction (`chan<-`, `<-chan`)2575. Default to unbuffered channels2586. Always include `ctx.Done()` in `select`2597. NEVER use `time.After` in loops -- use `time.NewTimer` + `Reset`260261### Channel vs Mutex vs Atomic262263| Scenario | Use | Why |264|----------|-----|-----|265| Passing data between goroutines | Channel | Communicates ownership transfer |266| Coordinating goroutine lifecycle | Channel + context | Clean shutdown with select |267| Protecting shared struct fields | `sync.Mutex` / `sync.RWMutex` | Simple critical sections |268| Simple counters, flags | `sync/atomic` | Lock-free, lower overhead |269| Many readers, few writers on a map | `sync.Map` | Optimized for read-heavy workloads |270| Caching expensive computations | `sync.Once` / `singleflight` | Execute once or deduplicate |271272### Concurrency Checklist273274Before spawning a goroutine, answer:275276- [ ] How will it exit?277- [ ] Can I signal it to stop?278- [ ] Can I wait for it?279- [ ] Who owns the channels?280- [ ] Should this be synchronous instead?281282See `references/concurrency-patterns.md` for pipelines, worker pools, errgroup, and sync primitives.283284## Performance285286Apply only to hot paths -- do NOT optimize speculatively.287288### Key Rules289290- Preallocate slices and maps when size is known: `make([]T, 0, n)`291- Prefer `strconv` over `fmt` for simple conversions (2x faster)292- Avoid repeated string-to-byte conversions -- convert once and reuse293- Use `strings.Builder` for string concatenation in loops294- Specify container capacity: `make(map[K]V, hint)`295- Use `b.ReportAllocs()` in benchmarks to track allocations296- Profile before optimizing: `go tool pprof`297298### Data Structure Selection299300| Need | Use | Why |301|------|-----|-----|302| Ordered collection, random access | Slice | Cache-friendly, growable |303| Key-value lookup | Map | O(1) average access |304| Fixed-size, compile-time known | Array | Value type, usable as map key |305| Priority queue | `container/heap` | Efficient insert/extract-min |306| String building | `strings.Builder` | No copy on `String()` |307| Bidirectional I/O | `bytes.Buffer` | Implements `io.Reader` and `io.Writer` |308309See `references/style-guide.md` for value vs pointer argument guidelines.310311## Security312313### Critical Rules314315- NEVER use `math/rand` for tokens or secrets -- use `crypto/rand`316- NEVER concatenate SQL strings -- use parameterized queries (`database/sql` with `?`)317- NEVER use `exec.Command("bash", "-c", userInput)` -- pass args separately318- NEVER hardcode secrets -- use environment variables or secret managers319- Use `html/template` for web output (auto-escaping), NEVER `text/template`320- Compare secrets with `crypto/subtle.ConstantTimeCompare`, not `==`321- Always run `go test -race ./...` in CI322- Run `govulncheck ./...` to check for known vulnerabilities323324### Quick Reference325326| Severity | Vulnerability | Defense |327|----------|--------------|---------|328| Critical | SQL injection | Parameterized queries with `database/sql` |329| Critical | Command injection | `exec.Command` with separate args |330| Critical | Hardcoded secrets | Environment variables or secret managers |331| High | XSS | `html/template` auto-escaping |332| High | Path traversal | `os.Root` (Go 1.24+), `filepath.Clean` |333| High | Weak crypto | `crypto/aes` GCM, `crypto/rand` |334| Medium | Timing attacks | `crypto/subtle.ConstantTimeCompare` |335| High | Race conditions | `sync.Mutex`, channels, `-race` flag |336337See `references/security-checklist.md` for the full security review checklist.338339## Validation Pipeline340341```bash342gofmt -s -w . # format343goimports -w . # organize imports344go vet ./... # static analysis345golangci-lint run # comprehensive linting346go test -race -cover ./... # test with race detection347govulncheck ./... # vulnerability scan348```349350### Recommended Linters (golangci-lint)351352Minimum set: `errcheck`, `govet`, `staticcheck`, `revive`, `goimports`. Add `gosec` for security analysis.353354## DO NOTs355356- Do NOT use `panic` for expected error conditions357- Do NOT discard errors with `_` (except explicitly justified cases)358- Do NOT use `init()` unless deterministic and side-effect-free359- Do NOT fire-and-forget goroutines -- every goroutine needs a shutdown mechanism360- Do NOT use `ALL_CAPS` for constants361- Do NOT use `this`/`self` for receivers362- Do NOT shadow built-in names (`error`, `string`, `len`, `cap`)363- Do NOT use mutable globals -- prefer dependency injection364- Do NOT embed types in public structs without careful consideration365- Do NOT use `reflect` unless absolutely necessary366367## IaC Tooling and Kubernetes Operators368369When developing Terraform providers, Kubernetes operators, or other IaC tooling in Go, apply these additional patterns.370371### Terraform Provider Development372373- Use the `terraform-plugin-framework` (not the deprecated SDKv2) for new providers374- Follow the `terraform-plugin-framework` resource lifecycle: `Create`, `Read`, `Update`, `Delete`375- Implement `ImportState` for all resources376- Use `terraform-plugin-testing` for acceptance tests with `resource.Test` and `resource.TestStep`377- Provider schemas MUST match the API 1:1 -- do NOT add computed convenience fields378- Use `context.Context` propagation in all CRUD methods379- Acceptance tests MUST be integration tests with real infrastructure (use build tags)380381```go382func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {383 var data ExampleResourceModel384 resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)385 if resp.Diagnostics.HasError() {386 return387 }388 // API call, map response to state389 resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)390}391```392393### Kubernetes Operator Development394395- Use `controller-runtime` (kubebuilder/operator-sdk) for operator scaffolding396- Reconcile loops MUST be idempotent -- same input produces same output regardless of current state397- Use `controllerutil.SetControllerReference` for owner references (automatic garbage collection)398- Implement `Finalizers` for cleanup of external resources399- Use `Status` subresource for reporting state (NOT spec fields)400- Use `controller-runtime`'s `client.Client` for API interactions (not `client-go` directly)401- CRDs MUST have validation via OpenAPI schema (kubebuilder markers)402- Use `envtest` for integration tests (spins up a real API server, no cluster needed)403404```go405func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {406 var obj MyResource407 if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {408 return ctrl.Result{}, client.IgnoreNotFound(err)409 }410411 // Idempotent reconciliation logic412413 return ctrl.Result{}, nil414}415```416417### Common Patterns for Both418419- Structured logging with `slog` or `logr` (controller-runtime's logger interface)420- Exponential backoff for API calls with `wait.ExponentialBackoff` or `ctrl.Result{RequeueAfter: ...}`421- Context propagation throughout the call chain422- Integration tests with real backends (not mocks for provider/operator behavior)423- `make generate` for code generation (deepcopy, CRD manifests, provider schemas)424425See `references/iac-tooling.md` for detailed patterns.426427## Philosophy -- Go Proverbs428429The [Go Proverbs](https://go-proverbs.github.io/) by Rob Pike capture the essence of Go's design philosophy. These are not suggestions -- they are the cultural foundation of the language:430431- **"Clear is better than clever."** -- Readability wins over elegance. If someone has to think hard to understand your code, simplify it.432- **"Don't communicate by sharing memory, share memory by communicating."** -- Use channels to transfer ownership, not mutexes to guard shared state.433- **"Concurrency is not parallelism."** -- Concurrency is about structure; parallelism is about execution. Design for concurrency, the runtime handles parallelism.434- **"Channels orchestrate; mutexes serialize."** -- Channels coordinate goroutine lifecycles; mutexes protect data. Choose based on the problem.435- **"The bigger the interface, the weaker the abstraction."** -- Small interfaces (`io.Reader`, `io.Writer`) are powerful. Large interfaces are hard to implement and hard to mock.436- **"Make the zero value useful."** -- `var buf bytes.Buffer` is ready to use. Design your types the same way.437- **"interface{} says nothing."** -- Use generics or concrete types. `any` erases type information and pushes errors to runtime.438- **"Errors are values."** -- Errors are not exceptions. They are regular values that can be inspected, compared, wrapped, and returned.439- **"Don't just check errors, handle them gracefully."** -- Wrap with context, return to the caller, or handle and recover. NEVER silently discard.440- **"A little copying is better than a little dependency."** -- A 5-line helper function copied into your project is better than importing a 500-line package.441- **"Reflection is never clear."** -- Avoid `reflect` unless absolutely necessary. It defeats type safety and confuses readers.442- **"Gofmt's style is no one's favorite, yet gofmt is everyone's favorite."** -- Consistency beats personal preference. Run `gofmt` and move on.443- **"Don't panic."** -- Return errors. Panics are for truly unrecoverable states, not for input validation.444- **"Design the architecture, name the components, document the details."** -- Architecture is about structure, naming is about clarity, documentation is about communication.445- **"Documentation is for users."** -- Write documentation that helps the consumer of your API, not the author.446- **"Cgo is not Go."** -- Cgo introduces build complexity, platform dependencies, and GC interaction issues. Avoid unless necessary.447- **"With the unsafe package there are no guarantees."** -- The `unsafe` package voids Go's memory safety guarantees. Use only with extreme care.448- **"Syscall must always be guarded with build tags."** -- Platform-specific code must be conditionally compiled.449450## Inspirations and References451452This skill synthesizes best practices from:453454- **[Uber Go Style Guide](https://github.com/uber-go/guide)** -- Production-tested conventions from one of the largest Go codebases455- **[samber/cc-skills-golang](https://github.com/samber/cc-skills-golang)** -- Comprehensive Claude Code skills for Go development (35 skills, 98% effectiveness)456- **[Effective Go](https://go.dev/doc/effective_go)** -- Official Go team guidance457- **[Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)** -- Community wiki of review patterns458- **[Go Proverbs](https://go-proverbs.github.io/)** -- Rob Pike's guiding principles459- **[cloudflare/terraform-provider-cloudflare](https://github.com/cloudflare/terraform-provider-cloudflare)** -- Reference implementation for large-scale Terraform providers (service-per-resource, schema patterns, test templates)460461## References462463See `references/` directory for:464- `style-guide.md` -- Detailed style rules, value vs pointer, imports, line breaking465- `naming-conventions.md` -- Comprehensive naming rules with examples and common mistakes466- `error-patterns.md` -- Error wrapping, sentinel errors, custom types, structured logging467- `testing-patterns.md` -- HTTP handler tests, mocking, benchmarks, fuzzing, fixtures468- `concurrency-patterns.md` -- Pipelines, worker pools, errgroup, sync primitives469- `security-checklist.md` -- Full security review checklist by domain470- `project-layout.md` -- Project structure examples by project type471- `terraform-provider.md` -- Terraform provider development (CRUD lifecycle, schemas, testing, service-per-resource)472- `kubernetes-operator.md` -- Kubernetes operator development (reconcile loops, CRDs, envtest, finalizers)473- `iac-tooling.md` -- Shared IaC patterns (API client, retry, logging, context propagation)474475---476> Source: [DiegoBulhoes/claude](https://github.com/DiegoBulhoes/claude) — distributed by [TomeVault](https://tomevault.io).477<!-- tomevault:4.0:skill_md:2026-06-16 -->