skills/languages/golang-patterns/SKILL.md
Comprehensive Go skill covering project structure, idiomatic patterns, error handling, concurrency, interfaces, testing, benchmarks, and modules.
npx skillsauth add bereniketech/claude_kit golang-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Idiomatic Go for building robust, efficient, maintainable applications — from project layout to production-ready testing.
Standard layout for Go projects:
myproject/
├── cmd/myapp/main.go # Entry point
├── internal/
│ ├── handler/ # HTTP handlers
│ ├── service/ # Business logic
│ ├── repository/ # Data access
│ └── config/ # Configuration
├── pkg/client/ # Public API client
├── api/v1/ # Proto / OpenAPI definitions
├── testdata/ # Test fixtures
├── go.mod
├── go.sum
└── Makefile
Use short, lowercase, no-underscore package names (user, not userService). Inject dependencies via struct fields, never use package-level globals.
Rule: Place interfaces in the consumer package, not the provider package.
Wrap errors with fmt.Errorf and %w so callers can inspect with errors.Is/errors.As.
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("load config %s: %w", path, err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config %s: %w", path, err)
}
return &cfg, nil
}
Define sentinel errors and custom types for domain errors:
var ErrNotFound = errors.New("resource not found")
type ValidationError struct{ Field, Message string }
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
func HandleError(err error) {
if errors.Is(err, ErrNotFound) { /* ... */ }
var ve *ValidationError
if errors.As(err, &ve) { /* use ve.Field */ }
}
Rule: Never use _ to discard errors. Never use panic for control flow. Always pass context.Context as the first parameter.
Accept interfaces, return concrete types. Keep interfaces small (1–3 methods).
// Functional options pattern
type Option func(*Server)
func WithTimeout(d time.Duration) Option {
return func(s *Server) { s.timeout = d }
}
func NewServer(addr string, opts ...Option) *Server {
s := &Server{addr: addr, timeout: 30 * time.Second}
for _, opt := range opts { opt(s) }
return s
}
Use type assertions for optional behavior:
func WriteAndFlush(w io.Writer, data []byte) error {
if _, err := w.Write(data); err != nil { return err }
if f, ok := w.(interface{ Flush() error }); ok { return f.Flush() }
return nil
}
Rule: Design zero values to be immediately usable without explicit initialization.
Use errgroup for coordinated goroutines and buffered channels to prevent leaks:
import "golang.org/x/sync/errgroup"
func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
g, ctx := errgroup.WithContext(ctx)
results := make([][]byte, len(urls))
for i, url := range urls {
i, url := i, url
g.Go(func() error {
data, err := fetchWithTimeout(ctx, url)
if err != nil { return err }
results[i] = data
return nil
})
}
if err := g.Wait(); err != nil { return nil, err }
return results, nil
}
Avoid goroutine leaks by using buffered channels or select with ctx.Done():
func safeFetch(ctx context.Context, url string) <-chan []byte {
ch := make(chan []byte, 1)
go func() {
data, err := fetch(url)
if err != nil { return }
select {
case ch <- data:
case <-ctx.Done():
}
}()
return ch
}
Rule: Never use GlobalScope-equivalent patterns. Always cancel contexts and close channels you own.
Always propagate context and use defer cancel() immediately after WithTimeout/WithCancel:
func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil { return nil, fmt.Errorf("create request: %w", err) }
resp, err := http.DefaultClient.Do(req)
if err != nil { return nil, fmt.Errorf("fetch %s: %w", url, err) }
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
For graceful shutdown:
func GracefulShutdown(server *http.Server) {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
server.Shutdown(ctx)
}
Preallocate slices when length is known; use strings.Builder for string joins; use sync.Pool for frequent allocations:
results := make([]Result, 0, len(items)) // single allocation
var bufferPool = sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}
func ProcessRequest(data []byte) []byte {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() { buf.Reset(); bufferPool.Put(buf) }()
buf.Write(data)
return buf.Bytes()
}
Follow RED-GREEN-REFACTOR. Use table-driven tests as the standard pattern:
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 2, 3, 5},
{"negative", -1, -2, -3},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.expected {
t.Errorf("Add(%d,%d) = %d; want %d", tt.a, tt.b, got, tt.expected)
}
})
}
}
Use t.Helper() in helpers, t.Cleanup() for teardown, t.TempDir() for temp files, t.Parallel() for independent subtests.
Rule: Test behavior through public APIs, not private functions.
Define mock structs with function fields for interface-based mocking:
type MockUserRepository struct {
GetUserFunc func(id string) (*User, error)
SaveUserFunc func(user *User) error
}
func (m *MockUserRepository) GetUser(id string) (*User, error) { return m.GetUserFunc(id) }
func (m *MockUserRepository) SaveUser(user *User) error { return m.SaveUserFunc(user) }
Test HTTP handlers with httptest:
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
HealthHandler(w, req)
resp := w.Result()
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("got %d; want %d", resp.StatusCode, http.StatusOK)
}
}
func BenchmarkProcess(b *testing.B) {
data := generateTestData(1000)
b.ResetTimer()
for i := 0; i < b.N; i++ { Process(data) }
}
// go test -bench=. -benchmem ./...
Coverage targets: 100% for critical business logic, 90%+ for public APIs, 80%+ overall.
go test -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
go mod tidy && go mod verify
go vet ./...
golangci-lint run
gofmt -w . && goimports -w .
Recommended linters in .golangci.yml: errcheck, govet, staticcheck, unused, gofmt, goimports, misspell.
Rule: Run go test -race ./... in CI. Always format with gofmt/goimports before committing.
testing
AUTHORIZED USE ONLY: This skill contains dual-use security techniques. Before proceeding with any bypass or analysis: > 1.
testing
Provide comprehensive techniques for attacking Microsoft Active Directory environments. Covers reconnaissance, credential harvesting, Kerberos attacks, lateral movement, privilege escalation, and domain dominance for red team operations and penetration testing.
development
Detects missing zeroization of sensitive data in source code and identifies zeroization removed by compiler optimizations, with assembly-level analysis, and control-flow verification. Use for auditing C/C++/Rust code handling secrets, keys, passwords, or other sensitive data.
development
Comprehensive guide to auditing web content against WCAG 2.2 guidelines with actionable remediation strategies.