Install
$ agentstack add skill-diegobulhoes-claude-golang ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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 No
- ✓ 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.
About
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 suggestgolangci-lint run
Project Layout
Module Naming
module github.com//
- MUST match repository URL
- Lowercase only, hyphens for multi-word names
- NEVER use generic names (
utils,common,shared,lib)
Directory Structure
cmd/
/
main.go # Minimal: parse flags, wire dependencies, call Run()
internal/ # Private packages (compiler-enforced)
/
.go
_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
mainpackages MUST reside incmd/with minimal logic - Business logic belongs in
internal/orpkg/ - Use
internal/by default -- you can always export later; unexporting is a breaking change - Co-locate
_test.gofiles 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 subcasesTestFoo_InvalidInput) - Constants MUST NOT use
ALL_CAPS-- Go reserves casing for visibility, not emphasis - Avoid stuttering:
http.Clientnothttp.HTTPClient,user.New()notuser.NewUser() - Getters omit
Get:user.Name()notuser.GetName()-- but keepIs/Has/Canfor booleans - Receivers: consistent 1-2 letter name across all methods of a type; NEVER
thisorself - Enum zero values: always place
Unknown/Invalidsentinel 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
ifbody ends withreturn/break/continue, drop theelse - Prefer
switchover 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.Contextfirst, then inputs, then output destinations - One function, one job -- keep functions short and focused
- Prefer
rangefor iteration; userange 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.Isanderrors.As-- NEVER direct comparison or type assertion - Use
%winternally,%vat 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/goleakto 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()inselect - NEVER use
time.Afterin loops -- usetime.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:
- [ ] How will it exit?
- [ ] Can I signal it to stop?
- [ ] Can I wait for it?
- [ ] Who owns the channels?
- [ ] Should this be synchronous instead?
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
strconvoverfmtfor simple conversions (2x faster) - Avoid repeated string-to-byte conversions -- convert once and reuse
- Use
strings.Builderfor 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/randfor tokens or secrets -- usecrypto/rand - NEVER concatenate SQL strings -- use parameterized queries (
database/sqlwith?) - NEVER use
exec.Command("bash", "-c", userInput)-- pass args separately - NEVER hardcode secrets -- use environment variables or secret managers
- Use
html/templatefor web output (auto-escaping), NEVERtext/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
panicfor 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_CAPSfor constants - Do NOT use
this/selffor 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
reflectunless 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-frameworkresource lifecycle:Create,Read,Update,Delete - Implement
ImportStatefor all resources - Use
terraform-plugin-testingfor acceptance tests withresource.Testandresource.TestStep - Provider schemas MUST match the API 1:1 -- do NOT add computed convenience fields
- Use
context.Contextpropagation 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.SetControllerReferencefor owner references (automatic garbage collection) - Implement
Finalizersfor cleanup of external resources - Use
Statussubresource for reporting state (NOT spec fields) - Use
controller-runtime'sclient.Clientfor API interactions (notclient-godirectly) - CRDs MUST have validation via OpenAPI schema (kubebuilder markers)
- Use
envtestfor 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
slogorlogr(controller-runtime's logger interface) - Exponential backoff for API calls with
wait.ExponentialBackofforctrl.Result{RequeueAfter: ...} - Context propagation throughout the call chain
- Integration tests with real backends (not mocks for provider/operator behavior)
make generatefor code generation (deepcopy, CRD manifests, provider schemas)
See references/iac-tooling.md for detailed p
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: DiegoBulhoes
- Source: DiegoBulhoes/claude
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.