From c69d66eaaadea774680142faf86f093826837b1b Mon Sep 17 00:00:00 2001 From: AlbertBui010 Date: Sat, 21 Feb 2026 13:22:24 +0700 Subject: [PATCH 1/2] feat(skills): add go-pro skill for Go development MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create .agent/skills/go-pro/SKILL.md with 15 sections covering: Go philosophy, project structure, concurrency, error handling, interface design, framework selection, database patterns, testing, performance, clean architecture, API design, logging, modules, anti-patterns, and decision checklist - Register go-pro in backend-specialist agent skills - Add go-pro to ARCHITECTURE.md skill tables (count 36β†’37) - Add Go/Golang keywords to intelligent-routing domain detection --- .agent/ARCHITECTURE.md | 9 +- .agent/agents/backend-specialist.md | 2 +- .agent/skills/go-pro/SKILL.md | 630 +++++++++++++++++++++ .agent/skills/intelligent-routing/SKILL.md | 1 + 4 files changed, 637 insertions(+), 5 deletions(-) create mode 100644 .agent/skills/go-pro/SKILL.md diff --git a/.agent/ARCHITECTURE.md b/.agent/ARCHITECTURE.md index 99ca60a1b..b06314e41 100644 --- a/.agent/ARCHITECTURE.md +++ b/.agent/ARCHITECTURE.md @@ -9,7 +9,7 @@ Antigravity Kit is a modular system consisting of: - **20 Specialist Agents** - Role-based AI personas -- **36 Skills** - Domain-specific knowledge modules +- **37 Skills** - Domain-specific knowledge modules - **11 Workflows** - Slash command procedures --- @@ -37,7 +37,7 @@ Specialist AI personas for different domains. | `orchestrator` | Multi-agent coordination | parallel-agents, behavioral-modes | | `project-planner` | Discovery, task planning | brainstorming, plan-writing, architecture | | `frontend-specialist` | Web UI/UX | frontend-design, react-best-practices, tailwind-patterns | -| `backend-specialist` | API, business logic | api-patterns, nodejs-best-practices, database-design | +| `backend-specialist` | API, business logic | api-patterns, nodejs-best-practices, go-pro, database-design | | `database-architect` | Schema, SQL | database-design, prisma-expert | | `mobile-developer` | iOS, Android, RN | mobile-design | | `game-developer` | Game logic, mechanics | game-development | @@ -57,7 +57,7 @@ Specialist AI personas for different domains. --- -## 🧩 Skills (36) +## 🧩 Skills (37) Modular knowledge domains that agents can load on-demand. based on task context. @@ -79,6 +79,7 @@ Modular knowledge domains that agents can load on-demand. based on task context. | `nestjs-expert` | NestJS modules, DI, decorators | | `nodejs-best-practices` | Node.js async, modules | | `python-patterns` | Python standards, FastAPI | +| `go-pro` | Go idioms, concurrency, clean arch | ### Database @@ -267,7 +268,7 @@ For details, see [scripts/README.md](scripts/README.md) | Metric | Value | | ------------------- | ----------------------------- | | **Total Agents** | 20 | -| **Total Skills** | 36 | +| **Total Skills** | 37 | | **Total Workflows** | 11 | | **Total Scripts** | 2 (master) + 18 (skill-level) | | **Coverage** | ~90% web/mobile development | diff --git a/.agent/agents/backend-specialist.md b/.agent/agents/backend-specialist.md index ad306ef7a..56624b4e5 100644 --- a/.agent/agents/backend-specialist.md +++ b/.agent/agents/backend-specialist.md @@ -3,7 +3,7 @@ name: backend-specialist description: Expert backend architect for Node.js, Python, and modern serverless/edge systems. Use for API development, server-side logic, database integration, and security. Triggers on backend, server, api, endpoint, database, auth. tools: Read, Grep, Glob, Bash, Edit, Write model: inherit -skills: clean-code, nodejs-best-practices, python-patterns, api-patterns, database-design, mcp-builder, lint-and-validate, powershell-windows, bash-linux, rust-pro +skills: clean-code, nodejs-best-practices, python-patterns, go-pro, api-patterns, database-design, mcp-builder, lint-and-validate, powershell-windows, bash-linux, rust-pro --- # Backend Development Architect diff --git a/.agent/skills/go-pro/SKILL.md b/.agent/skills/go-pro/SKILL.md new file mode 100644 index 000000000..33eb483a6 --- /dev/null +++ b/.agent/skills/go-pro/SKILL.md @@ -0,0 +1,630 @@ +--- +name: go-pro +description: Go development principles and decision-making. Idiomatic patterns, concurrency, clean architecture, project structure, testing, and performance. Teaches thinking, not copying. +allowed-tools: Read, Write, Edit, Glob, Grep +--- + +# Go Pro + +> Go development principles and decision-making for 2025. +> **Learn to THINK like a Go developer, not memorize syntax.** + +--- + +## ⚠️ How to Use This Skill + +This skill teaches **decision-making principles**, not fixed code to copy. + +- ASK user for framework/library preference when unclear +- Choose pattern based on PROJECT CONTEXT (scale, team, lifetime) +- Standard Library firstβ€”add dependencies only when justified + +--- + +## 1. Go Philosophy (Non-Negotiable) + +### Core Tenets + +``` +"Simplicity is complicated." β€” Rob Pike + +β”œβ”€β”€ Clear is better than clever +β”œβ”€β”€ A little copying is better than a little dependency +β”œβ”€β”€ Don't panic (literallyβ€”avoid panic in library code) +β”œβ”€β”€ Accept interfaces, return structs +β”œβ”€β”€ Make the zero value useful +└── Errors are values, not exceptions +``` + +### Standard Library First + +``` +Before adding a dependency, ask: +β”œβ”€β”€ Can `net/http` handle this? (Go 1.22+ has pattern routing) +β”œβ”€β”€ Can `encoding/json` handle this? +β”œβ”€β”€ Can `database/sql` handle this? +β”œβ”€β”€ Can `log/slog` handle this? (Go 1.21+ structured logging) +└── Will this dependency survive 5 years? + +Add dependency ONLY when: +β”œβ”€β”€ Significant complexity reduction (e.g., sqlc, pgx) +β”œβ”€β”€ Non-trivial to implement (e.g., JWT validation) +└── Well-maintained with clear ownership +``` + +--- + +## 2. Project Structure + +### Decision Tree + +``` +How big is the project? +β”‚ +β”œβ”€β”€ Single binary / Script / CLI tool +β”‚ └── Flat layout +β”‚ β”œβ”€β”€ main.go +β”‚ β”œβ”€β”€ handler.go +β”‚ β”œβ”€β”€ store.go +β”‚ └── go.mod +β”‚ +β”œβ”€β”€ Medium service (1 team, 1 domain) +β”‚ └── Standard layout +β”‚ β”œβ”€β”€ cmd/server/main.go +β”‚ β”œβ”€β”€ internal/ +β”‚ β”‚ β”œβ”€β”€ handler/ +β”‚ β”‚ β”œβ”€β”€ service/ +β”‚ β”‚ β”œβ”€β”€ repository/ +β”‚ β”‚ └── model/ +β”‚ β”œβ”€β”€ pkg/ # Only if reuse is real +β”‚ └── go.mod +β”‚ +└── Large system (multi-domain, multi-team) + └── Domain-driven layout + β”œβ”€β”€ cmd/ + β”‚ β”œβ”€β”€ api/main.go + β”‚ └── worker/main.go + β”œβ”€β”€ internal/ + β”‚ β”œβ”€β”€ user/ # Domain package + β”‚ β”‚ β”œβ”€β”€ handler.go + β”‚ β”‚ β”œβ”€β”€ service.go + β”‚ β”‚ β”œβ”€β”€ repository.go + β”‚ β”‚ └── model.go + β”‚ β”œβ”€β”€ order/ # Domain package + β”‚ └── platform/ # Shared infra + β”‚ β”œβ”€β”€ database/ + β”‚ β”œβ”€β”€ logger/ + β”‚ └── middleware/ + └── go.mod +``` + +### Key Rules + +``` +β”œβ”€β”€ cmd/ β†’ Entry points ONLY (wire up, start server) +β”œβ”€β”€ internal/ β†’ Private application code (compiler-enforced) +β”œβ”€β”€ pkg/ β†’ Truly reusable libraries (use sparingly!) +β”‚ +β”œβ”€β”€ NEVER put business logic in cmd/ +β”œβ”€β”€ NEVER put HTTP concerns in service layer +β”œβ”€β”€ NEVER import internal/ from another module +└── Keep main.go thin (< 50 lines ideally) +``` + +--- + +## 3. Concurrency Patterns + +### When to Use What + +| Pattern | Use When | +|---------|----------| +| `goroutine + channel` | Fan-out/fan-in, pipelines | +| `sync.WaitGroup` | Wait for N goroutines to complete | +| `errgroup.Group` | Wait + collect first error + cancel others | +| `sync.Mutex/RWMutex` | Protect shared state | +| `sync.Once` | One-time initialization | +| `context.Context` | Cancellation, timeout, request-scoped values | + +### The Golden Rules + +``` +Concurrency rules: +β”œβ”€β”€ Always pass context.Context as first parameter +β”œβ”€β”€ Never start a goroutine without knowing how it will stop +β”œβ”€β”€ Use errgroup for concurrent operations that can fail +β”œβ”€β”€ Prefer channels for communication, mutexes for state +β”œβ”€β”€ Don't communicate by sharing memoryβ€”share memory by communicating +β”œβ”€β”€ Always handle context cancellation +└── Use sync.Pool for frequently allocated objects (measure first!) + +Context propagation: +β”œβ”€β”€ Accept ctx in every function that does I/O +β”œβ”€β”€ Pass ctx to downstream calls +β”œβ”€β”€ Check ctx.Err() in long loops +β”œβ”€β”€ Use context.WithTimeout for external calls +└── NEVER store context in a struct +``` + +### Goroutine Lifecycle + +``` +ALWAYS ensure goroutine cleanup: +β”œβ”€β”€ Use context for cancellation +β”œβ”€β”€ Use done channels for signaling +β”œβ”€β”€ Defer cleanup in goroutines +β”œβ”€β”€ Prevent goroutine leaks +└── Log goroutine lifecycle in debug mode +``` + +--- + +## 4. Error Handling + +### Idiomatic Error Handling + +``` +Core patterns: +β”œβ”€β”€ if err != nil { return fmt.Errorf("doing X: %w", err) } +β”œβ”€β”€ Wrap errors with context using %w verb +β”œβ”€β”€ Use errors.Is() for sentinel error comparison +β”œβ”€β”€ Use errors.As() for type assertion on errors +β”œβ”€β”€ Return errors, don't panic (except truly unrecoverable) +└── Handle errors at the right level (don't swallow!) + +Error wrapping chain: +β”œβ”€β”€ Repository: fmt.Errorf("query user %d: %w", id, err) +β”œβ”€β”€ Service: fmt.Errorf("get user profile: %w", err) +β”œβ”€β”€ Handler: Log full error, return sanitized HTTP error +└── Client sees: {"error": "user not found"} (no internals!) +``` + +### Custom Error Types + +``` +When to create custom errors: +β”œβ”€β”€ Need to carry structured data (error code, field name) +β”œβ”€β”€ Need to distinguish error categories (NotFound, Conflict) +β”œβ”€β”€ Need to map to HTTP status codes +└── Shared across multiple packages + +When sentinel errors are enough: +β”œβ”€β”€ Simple "not found" / "already exists" cases +β”œβ”€β”€ Single package usage +└── No extra data needed +``` + +### Error Design Principles + +``` +β”œβ”€β”€ Errors should be opaque to callers (use Is/As, not string matching) +β”œβ”€β”€ Package-level sentinel: var ErrNotFound = errors.New("not found") +β”œβ”€β”€ Don't log AND return the same error (pick one per layer) +β”œβ”€β”€ Handler layer: log + return HTTP response +β”œβ”€β”€ Service layer: wrap + return +└── Repository layer: wrap + return +``` + +--- + +## 5. Interface Design + +### Core Principles + +``` +"The bigger the interface, the weaker the abstraction." β€” Rob Pike + +β”œβ”€β”€ Accept interfaces, return concrete types +β”œβ”€β”€ Define interfaces where they are USED, not implemented +β”œβ”€β”€ Keep interfaces small (1-3 methods ideal) +β”œβ”€β”€ Use implicit satisfaction (no "implements" keyword needed) +└── io.Reader / io.Writer are the gold standard + +Interface location: +β”œβ”€β”€ Consumer package defines the interface +β”œβ”€β”€ Producer package returns concrete struct +β”œβ”€β”€ This allows testing without mocks of the whole world +└── Example: service/ defines Repository interface, + repository/ returns *PostgresRepo struct +``` + +### Common Patterns + +``` +Repository interface (defined in service package): +β”œβ”€β”€ type UserRepository interface { +β”‚ GetByID(ctx context.Context, id int64) (*User, error) +β”‚ Create(ctx context.Context, user *User) error +β”‚ } +β”‚ +Service depends on interface: +β”œβ”€β”€ type UserService struct { +β”‚ repo UserRepository // interface, not concrete +β”‚ } +β”‚ +Testing becomes trivial: +└── Pass a mock/stub that satisfies the interface +``` + +--- + +## 6. Framework Selection (2025) + +### Decision Tree + +``` +What are you building? +β”‚ +β”œβ”€β”€ API with simple routing (Go 1.22+) +β”‚ └── net/http (ServeMux now supports patterns!) +β”‚ +β”œβ”€β”€ REST API with middleware needs +β”‚ └── Chi (lightweight, net/http compatible) +β”‚ +β”œβ”€β”€ High-performance API / Microservice +β”‚ └── Gin or Echo (battle-tested, fast) +β”‚ +β”œβ”€β”€ Maximum performance (benchmarks matter) +β”‚ └── Fiber (fasthttp-based, Express-like API) +β”‚ +β”œβ”€β”€ gRPC service +β”‚ └── google.golang.org/grpc + protobuf +β”‚ +└── CLI tool + └── cobra + viper +``` + +### Comparison Principles + +| Factor | net/http | Chi | Gin | Fiber | +|--------|----------|-----|-----|-------| +| **Best for** | Simple APIs, Go 1.22+ | Composable middleware | Production REST APIs | Max throughput | +| **Dependencies** | Zero | Minimal | Moderate | fasthttp (non-std) | +| **net/http compatible** | βœ… | βœ… | ❌ (own context) | ❌ (fasthttp) | +| **Middleware ecosystem** | Manual | Rich, composable | Rich, built-in | Growing | +| **Learning curve** | Low | Low | Low | Low | + +### Selection Questions to Ask: +1. Does the project need to stay net/http compatible? +2. Is raw throughput critical (>100K req/s)? +3. Does the team already know a specific framework? +4. How important is the middleware ecosystem? + +> **Default recommendation for new projects**: `net/http` (Go 1.22+) or `Chi` for composability. Only reach for Gin/Fiber when justified. + +--- + +## 7. Database Patterns + +### ORM/Driver Selection + +| Tool | Best For | +|------|----------| +| `database/sql` + raw SQL | Full control, simple queries | +| `sqlc` | Type-safe SQL β†’ Go code generation | +| `pgx` | PostgreSQL-specific features, performance | +| `GORM` | Rapid prototyping, complex relations | +| `Bun` | Modern ORM, good performance | +| `ent` | Graph-based schema, code generation | + +### Database Principles + +``` +β”œβ”€β”€ Use connection pooling (sql.DB handles this) +β”œβ”€β”€ Always use context-aware queries (QueryContext, ExecContext) +β”œβ”€β”€ Use prepared statements for repeated queries +β”œβ”€β”€ Close rows immediately: defer rows.Close() +β”œβ”€β”€ Scan into structs, not individual variables +β”œβ”€β”€ Use transactions for multi-step operations +β”œβ”€β”€ Set reasonable timeouts via context +└── Monitor connection pool metrics + +sqlc recommendation (for most projects): +β”œβ”€β”€ Write SQL β†’ Generate type-safe Go code +β”œβ”€β”€ No runtime reflection +β”œβ”€β”€ Catches SQL errors at compile time +β”œβ”€β”€ Works with PostgreSQL, MySQL, SQLite +└── Pairs perfectly with pgx driver +``` + +### Migration Strategy + +``` +β”œβ”€β”€ golang-migrate/migrate β†’ SQL-based, simple +β”œβ”€β”€ goose β†’ SQL or Go-based +β”œβ”€β”€ atlas β†’ Declarative, modern +β”‚ +β”œβ”€β”€ ALWAYS use versioned migrations +β”œβ”€β”€ NEVER modify existing migration files +β”œβ”€β”€ Test migrations up AND down +└── Run migrations separately from app startup +``` + +--- + +## 8. Testing + +### Table-Driven Tests (Idiomatic Go) + +``` +Pattern: +β”œβ”€β”€ Define test cases as slice of structs +β”œβ”€β”€ Loop through cases with t.Run(name, func) +β”œβ”€β”€ Each case: input β†’ expected output +β”œβ”€β”€ Name each case descriptively +β”œβ”€β”€ Add edge cases and error cases +└── Use t.Parallel() when tests are independent + +Benefits: +β”œβ”€β”€ Easy to add new cases +β”œβ”€β”€ Clear what's being tested +β”œβ”€β”€ Consistent structure across codebase +└── Great for debugging (run single case) +``` + +### Testing Strategy + +| Type | Purpose | Tools | +|------|---------|-------| +| **Unit** | Business logic, pure functions | `testing` (stdlib) | +| **Integration** | Database, external services | `testcontainers-go` | +| **Benchmark** | Performance measurement | `testing.B` | +| **Fuzz** | Input discovery (Go 1.18+) | `testing.F` | +| **E2E** | Full API workflows | `net/http/httptest` | + +### Testing Principles + +``` +β”œβ”€β”€ Use stdlib testing package first +β”œβ”€β”€ testify is OK for assertions (require/assert) +β”œβ”€β”€ Use httptest.NewServer for HTTP tests +β”œβ”€β”€ Use t.TempDir() for file-based tests +β”œβ”€β”€ Use testcontainers for real database tests +β”œβ”€β”€ Mock at interface boundaries only +β”œβ”€β”€ Benchmark before optimizing: go test -bench=. +β”œβ”€β”€ Fuzz test parsers and validators: go test -fuzz=. +└── Use t.Helper() in test helper functions +``` + +--- + +## 9. Performance + +### Profiling First (Measure, Don't Guess) + +``` +Tools: +β”œβ”€β”€ go tool pprof β†’ CPU, memory, goroutine profiling +β”œβ”€β”€ go test -bench β†’ Micro-benchmarks +β”œβ”€β”€ go test -benchmem β†’ Memory allocation counts +β”œβ”€β”€ runtime/trace β†’ Execution tracer +β”œβ”€β”€ expvar / metrics β†’ Runtime metrics endpoint +└── go build -gcflags="-m" β†’ Escape analysis + +Common optimizations (ONLY after profiling): +β”œβ”€β”€ Reduce allocations (sync.Pool, pre-allocate slices) +β”œβ”€β”€ Use strings.Builder for string concatenation +β”œβ”€β”€ Avoid interface{}/any in hot paths +β”œβ”€β”€ Use struct embedding to reduce pointer chasing +β”œβ”€β”€ Pre-size maps and slices: make([]T, 0, expectedCap) +β”œβ”€β”€ Use io.Reader/Writer for streaming (avoid loading all into memory) +└── Consider msgpack/protobuf over JSON for internal services +``` + +### Memory Layout + +``` +Struct field ordering matters: +β”œβ”€β”€ Group same-size fields together +β”œβ”€β”€ Larger fields first, smaller fields last +β”œβ”€β”€ Reduces padding, saves memory +β”œβ”€β”€ Use fieldalignment linter to detect issues +└── ONLY matters at scale (millions of structs) +``` + +--- + +## 10. Clean Architecture in Go + +### Layer Separation + +``` +Request Flow: +β”‚ +β”œβ”€β”€ Handler (Transport Layer) +β”‚ β”œβ”€β”€ HTTP/gRPC specifics +β”‚ β”œβ”€β”€ Parse request, validate input +β”‚ β”œβ”€β”€ Call service method +β”‚ └── Format response +β”‚ +β”œβ”€β”€ Service (Business Logic Layer) +β”‚ β”œβ”€β”€ Pure business rules +β”‚ β”œβ”€β”€ Depends on repository INTERFACES +β”‚ β”œβ”€β”€ No HTTP, no SQL +β”‚ └── Testable in isolation +β”‚ +β”œβ”€β”€ Repository (Data Access Layer) +β”‚ β”œβ”€β”€ Database queries +β”‚ β”œβ”€β”€ Implements repository interface +β”‚ β”œβ”€β”€ No business logic +β”‚ └── Returns domain models +β”‚ +└── Model (Domain Layer) + β”œβ”€β”€ Structs representing business entities + β”œβ”€β”€ No dependencies on other layers + └── Validation methods optional +``` + +### Dependency Injection + +``` +DI in Go (no magic, no framework needed): +β”œβ”€β”€ Constructor injection via New* functions +β”‚ func NewUserService(repo UserRepository) *UserService +β”‚ +β”œβ”€β”€ Wire up in cmd/main.go: +β”‚ db := database.Connect(cfg) +β”‚ userRepo := postgres.NewUserRepository(db) +β”‚ userService := service.NewUserService(userRepo) +β”‚ userHandler := handler.NewUserHandler(userService) +β”‚ +β”œβ”€β”€ For large projects, consider google/wire +β”‚ (compile-time DI code generation) +β”‚ +└── AVOID runtime DI containers (not idiomatic Go) +``` + +### When Clean Architecture is Overkill + +``` +Skip full layering when: +β”œβ”€β”€ Simple CRUD with < 5 entities +β”œβ”€β”€ CLI tools or scripts +β”œβ”€β”€ Prototypes / POCs +└── Single-person, short-lived projects + +Use full layering when: +β”œβ”€β”€ Multiple developers +β”œβ”€β”€ Complex business rules +β”œβ”€β”€ Long-lived project (> 1 year) +β”œβ”€β”€ Need to swap infrastructure +└── Extensive testing required +``` + +--- + +## 11. API Design Principles + +### HTTP Handler Patterns + +``` +Go 1.22+ ServeMux patterns: +β”œβ”€β”€ mux.HandleFunc("GET /users/{id}", handler.GetUser) +β”œβ”€β”€ mux.HandleFunc("POST /users", handler.CreateUser) +β”œβ”€β”€ Method + path in one string +└── Path parameters via r.PathValue("id") + +Middleware chain: +β”œβ”€β”€ func Middleware(next http.Handler) http.Handler +β”œβ”€β”€ Compose: Logger(Auth(RateLimit(handler))) +β”œβ”€β”€ Use for: logging, auth, CORS, recovery, metrics +└── Keep middleware focused (single responsibility) +``` + +### JSON Handling + +``` +Encoding/Decoding: +β”œβ”€β”€ Use json.NewDecoder(r.Body) for requests (streaming) +β”œβ”€β”€ Use json.NewEncoder(w) for responses +β”œβ”€β”€ Define struct tags: `json:"field_name,omitempty"` +β”œβ”€β”€ Use json.RawMessage for deferred parsing +β”œβ”€β”€ Limit request body size: http.MaxBytesReader +└── Set Content-Type header: "application/json" + +Validation: +β”œβ”€β”€ Validate after decoding, not during +β”œβ”€β”€ Use struct tags + validator package for complex rules +β”œβ”€β”€ Return 422 for validation errors with field-level details +└── Sanitize output (never expose internal struct fields) +``` + +--- + +## 12. Logging & Observability + +### Structured Logging (Go 1.21+) + +``` +Use log/slog (stdlib): +β”œβ”€β”€ slog.Info("user created", "user_id", id, "email", email) +β”œβ”€β”€ Structured key-value pairs +β”œβ”€β”€ JSON output for production +β”œβ”€β”€ Text output for development +β”œβ”€β”€ Create child loggers with With() +└── No need for zerolog/zap unless specific features needed + +Logging levels: +β”œβ”€β”€ Debug β†’ Development diagnostics +β”œβ”€β”€ Info β†’ Normal operations +β”œβ”€β”€ Warn β†’ Recoverable issues +β”œβ”€β”€ Error β†’ Failures requiring attention +└── NEVER log sensitive data (passwords, tokens, PII) +``` + +--- + +## 13. Go Modules & Dependencies + +### Module Management + +``` +β”œβ”€β”€ go mod init β†’ Start new module +β”œβ”€β”€ go mod tidy β†’ Clean up go.mod/go.sum +β”œβ”€β”€ go mod vendor β†’ Vendor dependencies (optional) +β”œβ”€β”€ go mod verify β†’ Verify integrity +β”‚ +β”œβ”€β”€ Pin major versions in go.mod +β”œβ”€β”€ Review go.sum changes in code review +β”œβ”€β”€ Use govulncheck for vulnerability scanning +β”œβ”€β”€ Prefer fewer, well-maintained dependencies +└── Check license compatibility +``` + +### Build & Release + +``` +β”œβ”€β”€ go build -ldflags "-s -w" β†’ Strip debug for smaller binary +β”œβ”€β”€ CGO_ENABLED=0 β†’ Static binary (no C dependencies) +β”œβ”€β”€ Use multi-stage Docker builds +β”œβ”€β”€ Cross-compile: GOOS=linux GOARCH=amd64 go build +└── Use goreleaser for automated releases +``` + +--- + +## 14. Anti-Patterns to Avoid + +### ❌ DON'T: +- Use `init()` for complex initialization (hard to test, surprising) +- Use `panic` in library code (return errors instead) +- Ignore errors with `_ = someFunc()` without explicit comment +- Use global mutable state (use dependency injection) +- Use `interface{}` / `any` when generics (Go 1.18+) work better +- Import from another project's `internal/` package +- Use Gin/Fiber for simple APIs where `net/http` suffices +- Store `context.Context` in struct fields +- Start goroutines without lifecycle management +- Use `sync.Mutex` when a channel is more appropriate + +### βœ… DO: +- Choose framework based on actual project needs +- Write table-driven tests for all business logic +- Use `context.Context` for cancellation and timeouts +- Profile before optimizing +- Keep interfaces small (1-3 methods) +- Use `errgroup` for concurrent operations +- Run `go vet`, `staticcheck`, `golangci-lint` in CI +- Use `sqlc` or `pgx` for database interactions +- Document exported functions and types +- Use `go generate` for code generation workflows + +--- + +## 15. Decision Checklist + +Before implementing: + +- [ ] **Asked user about framework preference?** +- [ ] **Chosen net/http vs framework for THIS context?** +- [ ] **Decided project structure scale?** (flat / standard / domain-driven) +- [ ] **Planned error handling strategy?** (sentinel / custom types / wrapping) +- [ ] **Identified concurrency needs?** (goroutines, channels, errgroup) +- [ ] **Chosen database approach?** (raw SQL, sqlc, GORM) +- [ ] **Planned testing strategy?** (table-driven, integration, benchmarks) +- [ ] **Considered clean architecture necessity?** (skip if simple CRUD) +- [ ] **Set up linting?** (golangci-lint with project config) + +--- + +> **Remember**: Go's power is in its simplicity. Don't fight the languageβ€”embrace `if err != nil`, small interfaces, explicit code, and the standard library. The best Go code looks boringβ€”and that's the point. diff --git a/.agent/skills/intelligent-routing/SKILL.md b/.agent/skills/intelligent-routing/SKILL.md index 5c8814b00..4edd8ec4a 100644 --- a/.agent/skills/intelligent-routing/SKILL.md +++ b/.agent/skills/intelligent-routing/SKILL.md @@ -105,6 +105,7 @@ function analyzeRequest(userMessage) { | **Security** | auth, login, jwt, password, hash, token | `security-auditor` | | **Frontend** | component, react, vue, css, html, tailwind | `frontend-specialist` | | **Backend** | api, server, express, fastapi, node | `backend-specialist` | +| **Go/Golang** | go, golang, goroutine, gin, fiber, chi | `backend-specialist` | | **Mobile** | react native, flutter, ios, android, expo | `mobile-developer` | | **Database** | prisma, sql, mongodb, schema, migration | `database-architect` | | **Testing** | test, jest, vitest, playwright, cypress | `test-engineer` | From 87a0ea196968701f831a9e7de61c6ababb79ef57 Mon Sep 17 00:00:00 2001 From: AlbertBui010 Date: Sat, 21 Feb 2026 13:41:50 +0700 Subject: [PATCH 2/2] refactor(go-pro): split into hybrid structure (core + references) - Rewrite SKILL.md to ~295 lines (core only): philosophy, project structure, error handling, interfaces, logging, modules, anti-patterns, decision checklist - Add 7 deep-dive reference files under references/: concurrency, frameworks, database, testing, performance, clean-architecture, api-design - SKILL.md includes index table for selective loading --- .agent/skills/go-pro/SKILL.md | 375 +----------------- .agent/skills/go-pro/references/api-design.md | 73 ++++ .../go-pro/references/clean-architecture.md | 79 ++++ .../skills/go-pro/references/concurrency.md | 74 ++++ .agent/skills/go-pro/references/database.md | 58 +++ .agent/skills/go-pro/references/frameworks.md | 67 ++++ .../skills/go-pro/references/performance.md | 69 ++++ .agent/skills/go-pro/references/testing.md | 67 ++++ 8 files changed, 507 insertions(+), 355 deletions(-) create mode 100644 .agent/skills/go-pro/references/api-design.md create mode 100644 .agent/skills/go-pro/references/clean-architecture.md create mode 100644 .agent/skills/go-pro/references/concurrency.md create mode 100644 .agent/skills/go-pro/references/database.md create mode 100644 .agent/skills/go-pro/references/frameworks.md create mode 100644 .agent/skills/go-pro/references/performance.md create mode 100644 .agent/skills/go-pro/references/testing.md diff --git a/.agent/skills/go-pro/SKILL.md b/.agent/skills/go-pro/SKILL.md index 33eb483a6..2ce3cdc9b 100644 --- a/.agent/skills/go-pro/SKILL.md +++ b/.agent/skills/go-pro/SKILL.md @@ -19,6 +19,20 @@ This skill teaches **decision-making principles**, not fixed code to copy. - Choose pattern based on PROJECT CONTEXT (scale, team, lifetime) - Standard Library firstβ€”add dependencies only when justified +### πŸ“ Deep-Dive References + +Load these **only when the specific topic is relevant** to the user's request: + +| Reference | When to Load | +|-----------|------| +| `references/concurrency.md` | Goroutines, channels, errgroup, context patterns | +| `references/frameworks.md` | Framework selection (net/http vs Chi vs Gin vs Fiber) | +| `references/database.md` | ORM/driver selection, sqlc, migrations, connection pooling | +| `references/testing.md` | Table-driven tests, benchmarks, fuzz, mocking | +| `references/performance.md` | pprof, escape analysis, memory layout, GC tuning | +| `references/clean-architecture.md` | Layered architecture, DI, domain-driven structure | +| `references/api-design.md` | HTTP handlers, JSON, middleware, graceful shutdown | + --- ## 1. Go Philosophy (Non-Negotiable) @@ -113,53 +127,7 @@ How big is the project? --- -## 3. Concurrency Patterns - -### When to Use What - -| Pattern | Use When | -|---------|----------| -| `goroutine + channel` | Fan-out/fan-in, pipelines | -| `sync.WaitGroup` | Wait for N goroutines to complete | -| `errgroup.Group` | Wait + collect first error + cancel others | -| `sync.Mutex/RWMutex` | Protect shared state | -| `sync.Once` | One-time initialization | -| `context.Context` | Cancellation, timeout, request-scoped values | - -### The Golden Rules - -``` -Concurrency rules: -β”œβ”€β”€ Always pass context.Context as first parameter -β”œβ”€β”€ Never start a goroutine without knowing how it will stop -β”œβ”€β”€ Use errgroup for concurrent operations that can fail -β”œβ”€β”€ Prefer channels for communication, mutexes for state -β”œβ”€β”€ Don't communicate by sharing memoryβ€”share memory by communicating -β”œβ”€β”€ Always handle context cancellation -└── Use sync.Pool for frequently allocated objects (measure first!) - -Context propagation: -β”œβ”€β”€ Accept ctx in every function that does I/O -β”œβ”€β”€ Pass ctx to downstream calls -β”œβ”€β”€ Check ctx.Err() in long loops -β”œβ”€β”€ Use context.WithTimeout for external calls -└── NEVER store context in a struct -``` - -### Goroutine Lifecycle - -``` -ALWAYS ensure goroutine cleanup: -β”œβ”€β”€ Use context for cancellation -β”œβ”€β”€ Use done channels for signaling -β”œβ”€β”€ Defer cleanup in goroutines -β”œβ”€β”€ Prevent goroutine leaks -└── Log goroutine lifecycle in debug mode -``` - ---- - -## 4. Error Handling +## 3. Error Handling ### Idiomatic Error Handling @@ -207,7 +175,7 @@ When sentinel errors are enough: --- -## 5. Interface Design +## 4. Interface Design ### Core Principles @@ -228,310 +196,9 @@ Interface location: repository/ returns *PostgresRepo struct ``` -### Common Patterns - -``` -Repository interface (defined in service package): -β”œβ”€β”€ type UserRepository interface { -β”‚ GetByID(ctx context.Context, id int64) (*User, error) -β”‚ Create(ctx context.Context, user *User) error -β”‚ } -β”‚ -Service depends on interface: -β”œβ”€β”€ type UserService struct { -β”‚ repo UserRepository // interface, not concrete -β”‚ } -β”‚ -Testing becomes trivial: -└── Pass a mock/stub that satisfies the interface -``` - ---- - -## 6. Framework Selection (2025) - -### Decision Tree - -``` -What are you building? -β”‚ -β”œβ”€β”€ API with simple routing (Go 1.22+) -β”‚ └── net/http (ServeMux now supports patterns!) -β”‚ -β”œβ”€β”€ REST API with middleware needs -β”‚ └── Chi (lightweight, net/http compatible) -β”‚ -β”œβ”€β”€ High-performance API / Microservice -β”‚ └── Gin or Echo (battle-tested, fast) -β”‚ -β”œβ”€β”€ Maximum performance (benchmarks matter) -β”‚ └── Fiber (fasthttp-based, Express-like API) -β”‚ -β”œβ”€β”€ gRPC service -β”‚ └── google.golang.org/grpc + protobuf -β”‚ -└── CLI tool - └── cobra + viper -``` - -### Comparison Principles - -| Factor | net/http | Chi | Gin | Fiber | -|--------|----------|-----|-----|-------| -| **Best for** | Simple APIs, Go 1.22+ | Composable middleware | Production REST APIs | Max throughput | -| **Dependencies** | Zero | Minimal | Moderate | fasthttp (non-std) | -| **net/http compatible** | βœ… | βœ… | ❌ (own context) | ❌ (fasthttp) | -| **Middleware ecosystem** | Manual | Rich, composable | Rich, built-in | Growing | -| **Learning curve** | Low | Low | Low | Low | - -### Selection Questions to Ask: -1. Does the project need to stay net/http compatible? -2. Is raw throughput critical (>100K req/s)? -3. Does the team already know a specific framework? -4. How important is the middleware ecosystem? - -> **Default recommendation for new projects**: `net/http` (Go 1.22+) or `Chi` for composability. Only reach for Gin/Fiber when justified. - ---- - -## 7. Database Patterns - -### ORM/Driver Selection - -| Tool | Best For | -|------|----------| -| `database/sql` + raw SQL | Full control, simple queries | -| `sqlc` | Type-safe SQL β†’ Go code generation | -| `pgx` | PostgreSQL-specific features, performance | -| `GORM` | Rapid prototyping, complex relations | -| `Bun` | Modern ORM, good performance | -| `ent` | Graph-based schema, code generation | - -### Database Principles - -``` -β”œβ”€β”€ Use connection pooling (sql.DB handles this) -β”œβ”€β”€ Always use context-aware queries (QueryContext, ExecContext) -β”œβ”€β”€ Use prepared statements for repeated queries -β”œβ”€β”€ Close rows immediately: defer rows.Close() -β”œβ”€β”€ Scan into structs, not individual variables -β”œβ”€β”€ Use transactions for multi-step operations -β”œβ”€β”€ Set reasonable timeouts via context -└── Monitor connection pool metrics - -sqlc recommendation (for most projects): -β”œβ”€β”€ Write SQL β†’ Generate type-safe Go code -β”œβ”€β”€ No runtime reflection -β”œβ”€β”€ Catches SQL errors at compile time -β”œβ”€β”€ Works with PostgreSQL, MySQL, SQLite -└── Pairs perfectly with pgx driver -``` - -### Migration Strategy - -``` -β”œβ”€β”€ golang-migrate/migrate β†’ SQL-based, simple -β”œβ”€β”€ goose β†’ SQL or Go-based -β”œβ”€β”€ atlas β†’ Declarative, modern -β”‚ -β”œβ”€β”€ ALWAYS use versioned migrations -β”œβ”€β”€ NEVER modify existing migration files -β”œβ”€β”€ Test migrations up AND down -└── Run migrations separately from app startup -``` - ---- - -## 8. Testing - -### Table-Driven Tests (Idiomatic Go) - -``` -Pattern: -β”œβ”€β”€ Define test cases as slice of structs -β”œβ”€β”€ Loop through cases with t.Run(name, func) -β”œβ”€β”€ Each case: input β†’ expected output -β”œβ”€β”€ Name each case descriptively -β”œβ”€β”€ Add edge cases and error cases -└── Use t.Parallel() when tests are independent - -Benefits: -β”œβ”€β”€ Easy to add new cases -β”œβ”€β”€ Clear what's being tested -β”œβ”€β”€ Consistent structure across codebase -└── Great for debugging (run single case) -``` - -### Testing Strategy - -| Type | Purpose | Tools | -|------|---------|-------| -| **Unit** | Business logic, pure functions | `testing` (stdlib) | -| **Integration** | Database, external services | `testcontainers-go` | -| **Benchmark** | Performance measurement | `testing.B` | -| **Fuzz** | Input discovery (Go 1.18+) | `testing.F` | -| **E2E** | Full API workflows | `net/http/httptest` | - -### Testing Principles - -``` -β”œβ”€β”€ Use stdlib testing package first -β”œβ”€β”€ testify is OK for assertions (require/assert) -β”œβ”€β”€ Use httptest.NewServer for HTTP tests -β”œβ”€β”€ Use t.TempDir() for file-based tests -β”œβ”€β”€ Use testcontainers for real database tests -β”œβ”€β”€ Mock at interface boundaries only -β”œβ”€β”€ Benchmark before optimizing: go test -bench=. -β”œβ”€β”€ Fuzz test parsers and validators: go test -fuzz=. -└── Use t.Helper() in test helper functions -``` - ---- - -## 9. Performance - -### Profiling First (Measure, Don't Guess) - -``` -Tools: -β”œβ”€β”€ go tool pprof β†’ CPU, memory, goroutine profiling -β”œβ”€β”€ go test -bench β†’ Micro-benchmarks -β”œβ”€β”€ go test -benchmem β†’ Memory allocation counts -β”œβ”€β”€ runtime/trace β†’ Execution tracer -β”œβ”€β”€ expvar / metrics β†’ Runtime metrics endpoint -└── go build -gcflags="-m" β†’ Escape analysis - -Common optimizations (ONLY after profiling): -β”œβ”€β”€ Reduce allocations (sync.Pool, pre-allocate slices) -β”œβ”€β”€ Use strings.Builder for string concatenation -β”œβ”€β”€ Avoid interface{}/any in hot paths -β”œβ”€β”€ Use struct embedding to reduce pointer chasing -β”œβ”€β”€ Pre-size maps and slices: make([]T, 0, expectedCap) -β”œβ”€β”€ Use io.Reader/Writer for streaming (avoid loading all into memory) -└── Consider msgpack/protobuf over JSON for internal services -``` - -### Memory Layout - -``` -Struct field ordering matters: -β”œβ”€β”€ Group same-size fields together -β”œβ”€β”€ Larger fields first, smaller fields last -β”œβ”€β”€ Reduces padding, saves memory -β”œβ”€β”€ Use fieldalignment linter to detect issues -└── ONLY matters at scale (millions of structs) -``` - ---- - -## 10. Clean Architecture in Go - -### Layer Separation - -``` -Request Flow: -β”‚ -β”œβ”€β”€ Handler (Transport Layer) -β”‚ β”œβ”€β”€ HTTP/gRPC specifics -β”‚ β”œβ”€β”€ Parse request, validate input -β”‚ β”œβ”€β”€ Call service method -β”‚ └── Format response -β”‚ -β”œβ”€β”€ Service (Business Logic Layer) -β”‚ β”œβ”€β”€ Pure business rules -β”‚ β”œβ”€β”€ Depends on repository INTERFACES -β”‚ β”œβ”€β”€ No HTTP, no SQL -β”‚ └── Testable in isolation -β”‚ -β”œβ”€β”€ Repository (Data Access Layer) -β”‚ β”œβ”€β”€ Database queries -β”‚ β”œβ”€β”€ Implements repository interface -β”‚ β”œβ”€β”€ No business logic -β”‚ └── Returns domain models -β”‚ -└── Model (Domain Layer) - β”œβ”€β”€ Structs representing business entities - β”œβ”€β”€ No dependencies on other layers - └── Validation methods optional -``` - -### Dependency Injection - -``` -DI in Go (no magic, no framework needed): -β”œβ”€β”€ Constructor injection via New* functions -β”‚ func NewUserService(repo UserRepository) *UserService -β”‚ -β”œβ”€β”€ Wire up in cmd/main.go: -β”‚ db := database.Connect(cfg) -β”‚ userRepo := postgres.NewUserRepository(db) -β”‚ userService := service.NewUserService(userRepo) -β”‚ userHandler := handler.NewUserHandler(userService) -β”‚ -β”œβ”€β”€ For large projects, consider google/wire -β”‚ (compile-time DI code generation) -β”‚ -└── AVOID runtime DI containers (not idiomatic Go) -``` - -### When Clean Architecture is Overkill - -``` -Skip full layering when: -β”œβ”€β”€ Simple CRUD with < 5 entities -β”œβ”€β”€ CLI tools or scripts -β”œβ”€β”€ Prototypes / POCs -└── Single-person, short-lived projects - -Use full layering when: -β”œβ”€β”€ Multiple developers -β”œβ”€β”€ Complex business rules -β”œβ”€β”€ Long-lived project (> 1 year) -β”œβ”€β”€ Need to swap infrastructure -└── Extensive testing required -``` - ---- - -## 11. API Design Principles - -### HTTP Handler Patterns - -``` -Go 1.22+ ServeMux patterns: -β”œβ”€β”€ mux.HandleFunc("GET /users/{id}", handler.GetUser) -β”œβ”€β”€ mux.HandleFunc("POST /users", handler.CreateUser) -β”œβ”€β”€ Method + path in one string -└── Path parameters via r.PathValue("id") - -Middleware chain: -β”œβ”€β”€ func Middleware(next http.Handler) http.Handler -β”œβ”€β”€ Compose: Logger(Auth(RateLimit(handler))) -β”œβ”€β”€ Use for: logging, auth, CORS, recovery, metrics -└── Keep middleware focused (single responsibility) -``` - -### JSON Handling - -``` -Encoding/Decoding: -β”œβ”€β”€ Use json.NewDecoder(r.Body) for requests (streaming) -β”œβ”€β”€ Use json.NewEncoder(w) for responses -β”œβ”€β”€ Define struct tags: `json:"field_name,omitempty"` -β”œβ”€β”€ Use json.RawMessage for deferred parsing -β”œβ”€β”€ Limit request body size: http.MaxBytesReader -└── Set Content-Type header: "application/json" - -Validation: -β”œβ”€β”€ Validate after decoding, not during -β”œβ”€β”€ Use struct tags + validator package for complex rules -β”œβ”€β”€ Return 422 for validation errors with field-level details -└── Sanitize output (never expose internal struct fields) -``` - --- -## 12. Logging & Observability +## 5. Logging & Observability ### Structured Logging (Go 1.21+) @@ -554,9 +221,7 @@ Logging levels: --- -## 13. Go Modules & Dependencies - -### Module Management +## 6. Go Modules & Dependencies ``` β”œβ”€β”€ go mod init β†’ Start new module @@ -583,7 +248,7 @@ Logging levels: --- -## 14. Anti-Patterns to Avoid +## 7. Anti-Patterns to Avoid ### ❌ DON'T: - Use `init()` for complex initialization (hard to test, surprising) @@ -611,7 +276,7 @@ Logging levels: --- -## 15. Decision Checklist +## 8. Decision Checklist Before implementing: diff --git a/.agent/skills/go-pro/references/api-design.md b/.agent/skills/go-pro/references/api-design.md new file mode 100644 index 000000000..0804abbc4 --- /dev/null +++ b/.agent/skills/go-pro/references/api-design.md @@ -0,0 +1,73 @@ +# API Design Principles + +> Deep-dive reference for Go HTTP API design. Load when user builds REST APIs, middleware, or JSON handlers. + +## HTTP Handler Patterns + +``` +Go 1.22+ ServeMux patterns: +β”œβ”€β”€ mux.HandleFunc("GET /users/{id}", handler.GetUser) +β”œβ”€β”€ mux.HandleFunc("POST /users", handler.CreateUser) +β”œβ”€β”€ Method + path in one string +└── Path parameters via r.PathValue("id") + +Middleware chain: +β”œβ”€β”€ func Middleware(next http.Handler) http.Handler +β”œβ”€β”€ Compose: Logger(Auth(RateLimit(handler))) +β”œβ”€β”€ Use for: logging, auth, CORS, recovery, metrics +└── Keep middleware focused (single responsibility) +``` + +## JSON Handling + +``` +Encoding/Decoding: +β”œβ”€β”€ Use json.NewDecoder(r.Body) for requests (streaming) +β”œβ”€β”€ Use json.NewEncoder(w) for responses +β”œβ”€β”€ Define struct tags: `json:"field_name,omitempty"` +β”œβ”€β”€ Use json.RawMessage for deferred parsing +β”œβ”€β”€ Limit request body size: http.MaxBytesReader +└── Set Content-Type header: "application/json" + +Validation: +β”œβ”€β”€ Validate after decoding, not during +β”œβ”€β”€ Use struct tags + validator package for complex rules +β”œβ”€β”€ Return 422 for validation errors with field-level details +└── Sanitize output (never expose internal struct fields) +``` + +## Response Format + +``` +Consistent API response: +β”œβ”€β”€ Success: { "data": {...} } +β”œβ”€β”€ Error: { "error": { "code": "NOT_FOUND", "message": "..." } } +β”œβ”€β”€ List: { "data": [...], "pagination": { "total": N, "page": 1 } } +β”‚ +β”œβ”€β”€ Always return JSON (even for errors) +β”œβ”€β”€ Use appropriate HTTP status codes +└── Include request ID in response headers +``` + +## Error Response Mapping + +| Domain Error | HTTP Status | Error Code | +|-------------|-------------|------------| +| Not found | 404 | `NOT_FOUND` | +| Validation failed | 422 | `VALIDATION_ERROR` | +| Already exists | 409 | `CONFLICT` | +| Unauthorized | 401 | `UNAUTHORIZED` | +| Forbidden | 403 | `FORBIDDEN` | +| Internal error | 500 | `INTERNAL_ERROR` | + +## Graceful Shutdown + +``` +Production servers must handle shutdown: +β”œβ”€β”€ Listen for os.Signal (SIGINT, SIGTERM) +β”œβ”€β”€ Call server.Shutdown(ctx) with timeout +β”œβ”€β”€ Finish in-flight requests +β”œβ”€β”€ Close database connections +β”œβ”€β”€ Flush logs +└── Exit cleanly +``` diff --git a/.agent/skills/go-pro/references/clean-architecture.md b/.agent/skills/go-pro/references/clean-architecture.md new file mode 100644 index 000000000..23a47f0c3 --- /dev/null +++ b/.agent/skills/go-pro/references/clean-architecture.md @@ -0,0 +1,79 @@ +# Clean Architecture in Go + +> Deep-dive reference for layered architecture and dependency injection in Go. Load when user designs service structure or needs DI patterns. + +## Layer Separation + +``` +Request Flow: +β”‚ +β”œβ”€β”€ Handler (Transport Layer) +β”‚ β”œβ”€β”€ HTTP/gRPC specifics +β”‚ β”œβ”€β”€ Parse request, validate input +β”‚ β”œβ”€β”€ Call service method +β”‚ └── Format response +β”‚ +β”œβ”€β”€ Service (Business Logic Layer) +β”‚ β”œβ”€β”€ Pure business rules +β”‚ β”œβ”€β”€ Depends on repository INTERFACES +β”‚ β”œβ”€β”€ No HTTP, no SQL +β”‚ └── Testable in isolation +β”‚ +β”œβ”€β”€ Repository (Data Access Layer) +β”‚ β”œβ”€β”€ Database queries +β”‚ β”œβ”€β”€ Implements repository interface +β”‚ β”œβ”€β”€ No business logic +β”‚ └── Returns domain models +β”‚ +└── Model (Domain Layer) + β”œβ”€β”€ Structs representing business entities + β”œβ”€β”€ No dependencies on other layers + └── Validation methods optional +``` + +## Dependency Injection + +``` +DI in Go (no magic, no framework needed): +β”œβ”€β”€ Constructor injection via New* functions +β”‚ func NewUserService(repo UserRepository) *UserService +β”‚ +β”œβ”€β”€ Wire up in cmd/main.go: +β”‚ db := database.Connect(cfg) +β”‚ userRepo := postgres.NewUserRepository(db) +β”‚ userService := service.NewUserService(userRepo) +β”‚ userHandler := handler.NewUserHandler(userService) +β”‚ +β”œβ”€β”€ For large projects, consider google/wire +β”‚ (compile-time DI code generation) +β”‚ +└── AVOID runtime DI containers (not idiomatic Go) +``` + +## When Clean Architecture is Overkill + +``` +Skip full layering when: +β”œβ”€β”€ Simple CRUD with < 5 entities +β”œβ”€β”€ CLI tools or scripts +β”œβ”€β”€ Prototypes / POCs +└── Single-person, short-lived projects + +Use full layering when: +β”œβ”€β”€ Multiple developers +β”œβ”€β”€ Complex business rules +β”œβ”€β”€ Long-lived project (> 1 year) +β”œβ”€β”€ Need to swap infrastructure +└── Extensive testing required +``` + +## Directory Mapping + +``` +Clean architecture β†’ Go project: +β”œβ”€β”€ Domain/Model β†’ internal/domain/ or internal/model/ +β”œβ”€β”€ Use Cases β†’ internal/service/ +β”œβ”€β”€ Interfaces β†’ internal/handler/ (HTTP), internal/grpc/ (gRPC) +β”œβ”€β”€ Infrastructure β†’ internal/repository/, internal/platform/ +└── Entry point β†’ cmd/server/main.go (wiring) +``` diff --git a/.agent/skills/go-pro/references/concurrency.md b/.agent/skills/go-pro/references/concurrency.md new file mode 100644 index 000000000..68c4baf2f --- /dev/null +++ b/.agent/skills/go-pro/references/concurrency.md @@ -0,0 +1,74 @@ +# Concurrency Patterns + +> Deep-dive reference for Go concurrency. Load when user works with goroutines, channels, or parallel processing. + +## When to Use What + +| Pattern | Use When | +|---------|----------| +| `goroutine + channel` | Fan-out/fan-in, pipelines | +| `sync.WaitGroup` | Wait for N goroutines to complete | +| `errgroup.Group` | Wait + collect first error + cancel others | +| `sync.Mutex/RWMutex` | Protect shared state | +| `sync.Once` | One-time initialization | +| `context.Context` | Cancellation, timeout, request-scoped values | + +## The Golden Rules + +``` +Concurrency rules: +β”œβ”€β”€ Always pass context.Context as first parameter +β”œβ”€β”€ Never start a goroutine without knowing how it will stop +β”œβ”€β”€ Use errgroup for concurrent operations that can fail +β”œβ”€β”€ Prefer channels for communication, mutexes for state +β”œβ”€β”€ Don't communicate by sharing memoryβ€”share memory by communicating +β”œβ”€β”€ Always handle context cancellation +└── Use sync.Pool for frequently allocated objects (measure first!) + +Context propagation: +β”œβ”€β”€ Accept ctx in every function that does I/O +β”œβ”€β”€ Pass ctx to downstream calls +β”œβ”€β”€ Check ctx.Err() in long loops +β”œβ”€β”€ Use context.WithTimeout for external calls +└── NEVER store context in a struct +``` + +## Goroutine Lifecycle + +``` +ALWAYS ensure goroutine cleanup: +β”œβ”€β”€ Use context for cancellation +β”œβ”€β”€ Use done channels for signaling +β”œβ”€β”€ Defer cleanup in goroutines +β”œβ”€β”€ Prevent goroutine leaks +└── Log goroutine lifecycle in debug mode +``` + +## Common Patterns + +### Fan-Out / Fan-In +``` +Use when: Processing N items concurrently, collecting results +β”œβ”€β”€ Spawn N goroutines (fan-out) +β”œβ”€β”€ Collect results via channel (fan-in) +β”œβ”€β”€ Use errgroup to handle errors +└── Limit concurrency with semaphore channel +``` + +### Pipeline +``` +Use when: Multi-stage data processing +β”œβ”€β”€ Each stage: goroutine reading from input channel +β”œβ”€β”€ Process β†’ send to output channel +β”œβ”€β”€ Close output channel when input is exhausted +└── Cancel pipeline via context +``` + +### Worker Pool +``` +Use when: Rate-limiting concurrent work +β”œβ”€β”€ Fixed number of goroutines reading from job channel +β”œβ”€β”€ Results sent to result channel +β”œβ”€β”€ Backpressure via buffered channels +└── Graceful shutdown via context cancellation +``` diff --git a/.agent/skills/go-pro/references/database.md b/.agent/skills/go-pro/references/database.md new file mode 100644 index 000000000..7c58ddac0 --- /dev/null +++ b/.agent/skills/go-pro/references/database.md @@ -0,0 +1,58 @@ +# Database Patterns + +> Deep-dive reference for Go database access. Load when user works with SQL, ORM, or database design. + +## ORM/Driver Selection + +| Tool | Best For | +|------|----------| +| `database/sql` + raw SQL | Full control, simple queries | +| `sqlc` | Type-safe SQL β†’ Go code generation | +| `pgx` | PostgreSQL-specific features, performance | +| `GORM` | Rapid prototyping, complex relations | +| `Bun` | Modern ORM, good performance | +| `ent` | Graph-based schema, code generation | + +## Database Principles + +``` +β”œβ”€β”€ Use connection pooling (sql.DB handles this) +β”œβ”€β”€ Always use context-aware queries (QueryContext, ExecContext) +β”œβ”€β”€ Use prepared statements for repeated queries +β”œβ”€β”€ Close rows immediately: defer rows.Close() +β”œβ”€β”€ Scan into structs, not individual variables +β”œβ”€β”€ Use transactions for multi-step operations +β”œβ”€β”€ Set reasonable timeouts via context +└── Monitor connection pool metrics + +sqlc recommendation (for most projects): +β”œβ”€β”€ Write SQL β†’ Generate type-safe Go code +β”œβ”€β”€ No runtime reflection +β”œβ”€β”€ Catches SQL errors at compile time +β”œβ”€β”€ Works with PostgreSQL, MySQL, SQLite +└── Pairs perfectly with pgx driver +``` + +## Migration Strategy + +``` +β”œβ”€β”€ golang-migrate/migrate β†’ SQL-based, simple +β”œβ”€β”€ goose β†’ SQL or Go-based +β”œβ”€β”€ atlas β†’ Declarative, modern +β”‚ +β”œβ”€β”€ ALWAYS use versioned migrations +β”œβ”€β”€ NEVER modify existing migration files +β”œβ”€β”€ Test migrations up AND down +└── Run migrations separately from app startup +``` + +## Connection Pool Tuning + +``` +sql.DB configuration: +β”œβ”€β”€ SetMaxOpenConns() β†’ Match your DB max connections +β”œβ”€β”€ SetMaxIdleConns() β†’ ~25% of max open +β”œβ”€β”€ SetConnMaxLifetime() β†’ 5-15 minutes +β”œβ”€β”€ SetConnMaxIdleTime() β†’ 1-3 minutes +└── Monitor with db.Stats() +``` diff --git a/.agent/skills/go-pro/references/frameworks.md b/.agent/skills/go-pro/references/frameworks.md new file mode 100644 index 000000000..11f5a4953 --- /dev/null +++ b/.agent/skills/go-pro/references/frameworks.md @@ -0,0 +1,67 @@ +# Framework Selection (2025) + +> Deep-dive reference for Go web framework selection. Load when user needs to choose or compare frameworks. + +## Decision Tree + +``` +What are you building? +β”‚ +β”œβ”€β”€ API with simple routing (Go 1.22+) +β”‚ └── net/http (ServeMux now supports patterns!) +β”‚ +β”œβ”€β”€ REST API with middleware needs +β”‚ └── Chi (lightweight, net/http compatible) +β”‚ +β”œβ”€β”€ High-performance API / Microservice +β”‚ └── Gin or Echo (battle-tested, fast) +β”‚ +β”œβ”€β”€ Maximum performance (benchmarks matter) +β”‚ └── Fiber (fasthttp-based, Express-like API) +β”‚ +β”œβ”€β”€ gRPC service +β”‚ └── google.golang.org/grpc + protobuf +β”‚ +└── CLI tool + └── cobra + viper +``` + +## Comparison Principles + +| Factor | net/http | Chi | Gin | Fiber | +|--------|----------|-----|-----|-------| +| **Best for** | Simple APIs, Go 1.22+ | Composable middleware | Production REST APIs | Max throughput | +| **Dependencies** | Zero | Minimal | Moderate | fasthttp (non-std) | +| **net/http compatible** | βœ… | βœ… | ❌ (own context) | ❌ (fasthttp) | +| **Middleware ecosystem** | Manual | Rich, composable | Rich, built-in | Growing | +| **Learning curve** | Low | Low | Low | Low | + +## Selection Questions to Ask: +1. Does the project need to stay net/http compatible? +2. Is raw throughput critical (>100K req/s)? +3. Does the team already know a specific framework? +4. How important is the middleware ecosystem? + +> **Default recommendation for new projects**: `net/http` (Go 1.22+) or `Chi` for composability. Only reach for Gin/Fiber when justified. + +## Go 1.22+ ServeMux Patterns + +``` +New routing capabilities: +β”œβ”€β”€ mux.HandleFunc("GET /users/{id}", handler.GetUser) +β”œβ”€β”€ mux.HandleFunc("POST /users", handler.CreateUser) +β”œβ”€β”€ Method + path in one string +β”œβ”€β”€ Path parameters via r.PathValue("id") +β”œβ”€β”€ Precedence rules: more specific patterns win +└── No need for Chi/Gorilla for simple routing anymore +``` + +## Middleware Pattern + +``` +Standard middleware signature: +β”œβ”€β”€ func Middleware(next http.Handler) http.Handler +β”œβ”€β”€ Compose: Logger(Auth(RateLimit(handler))) +β”œβ”€β”€ Use for: logging, auth, CORS, recovery, metrics +└── Keep middleware focused (single responsibility) +``` diff --git a/.agent/skills/go-pro/references/performance.md b/.agent/skills/go-pro/references/performance.md new file mode 100644 index 000000000..5b8291d25 --- /dev/null +++ b/.agent/skills/go-pro/references/performance.md @@ -0,0 +1,69 @@ +# Performance + +> Deep-dive reference for Go performance profiling and optimization. Load when user needs to profile or optimize Go code. + +## Profiling First (Measure, Don't Guess) + +``` +Tools: +β”œβ”€β”€ go tool pprof β†’ CPU, memory, goroutine profiling +β”œβ”€β”€ go test -bench β†’ Micro-benchmarks +β”œβ”€β”€ go test -benchmem β†’ Memory allocation counts +β”œβ”€β”€ runtime/trace β†’ Execution tracer +β”œβ”€β”€ expvar / metrics β†’ Runtime metrics endpoint +└── go build -gcflags="-m" β†’ Escape analysis + +Common optimizations (ONLY after profiling): +β”œβ”€β”€ Reduce allocations (sync.Pool, pre-allocate slices) +β”œβ”€β”€ Use strings.Builder for string concatenation +β”œβ”€β”€ Avoid interface{}/any in hot paths +β”œβ”€β”€ Use struct embedding to reduce pointer chasing +β”œβ”€β”€ Pre-size maps and slices: make([]T, 0, expectedCap) +β”œβ”€β”€ Use io.Reader/Writer for streaming (avoid loading all into memory) +└── Consider msgpack/protobuf over JSON for internal services +``` + +## Memory Layout + +``` +Struct field ordering matters: +β”œβ”€β”€ Group same-size fields together +β”œβ”€β”€ Larger fields first, smaller fields last +β”œβ”€β”€ Reduces padding, saves memory +β”œβ”€β”€ Use fieldalignment linter to detect issues +└── ONLY matters at scale (millions of structs) +``` + +## Escape Analysis + +``` +Understanding heap vs stack: +β”œβ”€β”€ go build -gcflags="-m" shows escape decisions +β”œβ”€β”€ Variables that escape to heap = allocation = GC pressure +β”œβ”€β”€ Returning pointer to local variable β†’ escapes +β”œβ”€β”€ Interface conversion β†’ may escape +β”œβ”€β”€ Closures capturing variables β†’ may escape +└── Reducing escapes = reducing GC pauses +``` + +## Benchmark Patterns + +``` +Writing benchmarks: +β”œβ”€β”€ func BenchmarkXxx(b *testing.B) { for i := 0; i < b.N; i++ { ... } } +β”œβ”€β”€ Use b.ResetTimer() after setup +β”œβ”€β”€ Use b.ReportAllocs() for allocation tracking +β”œβ”€β”€ Compare with benchstat for statistical significance +β”œβ”€β”€ Run: go test -bench=. -benchmem -count=10 +└── Profile: go test -bench=. -cpuprofile=cpu.prof +``` + +## GC Tuning + +``` +β”œβ”€β”€ GOGC=100 (default) β€” collect when heap doubles +β”œβ”€β”€ GOMEMLIMIT (Go 1.19+) β€” set soft memory limit +β”œβ”€β”€ Monitor with runtime.ReadMemStats() +β”œβ”€β”€ Reduce allocations > tuning GC +└── Use sync.Pool for frequently allocated objects +``` diff --git a/.agent/skills/go-pro/references/testing.md b/.agent/skills/go-pro/references/testing.md new file mode 100644 index 000000000..275519d1a --- /dev/null +++ b/.agent/skills/go-pro/references/testing.md @@ -0,0 +1,67 @@ +# Testing + +> Deep-dive reference for Go testing patterns. Load when user writes tests, benchmarks, or fuzz tests. + +## Table-Driven Tests (Idiomatic Go) + +``` +Pattern: +β”œβ”€β”€ Define test cases as slice of structs +β”œβ”€β”€ Loop through cases with t.Run(name, func) +β”œβ”€β”€ Each case: input β†’ expected output +β”œβ”€β”€ Name each case descriptively +β”œβ”€β”€ Add edge cases and error cases +└── Use t.Parallel() when tests are independent + +Benefits: +β”œβ”€β”€ Easy to add new cases +β”œβ”€β”€ Clear what's being tested +β”œβ”€β”€ Consistent structure across codebase +└── Great for debugging (run single case) +``` + +## Testing Strategy + +| Type | Purpose | Tools | +|------|---------|-------| +| **Unit** | Business logic, pure functions | `testing` (stdlib) | +| **Integration** | Database, external services | `testcontainers-go` | +| **Benchmark** | Performance measurement | `testing.B` | +| **Fuzz** | Input discovery (Go 1.18+) | `testing.F` | +| **E2E** | Full API workflows | `net/http/httptest` | + +## Testing Principles + +``` +β”œβ”€β”€ Use stdlib testing package first +β”œβ”€β”€ testify is OK for assertions (require/assert) +β”œβ”€β”€ Use httptest.NewServer for HTTP tests +β”œβ”€β”€ Use t.TempDir() for file-based tests +β”œβ”€β”€ Use testcontainers for real database tests +β”œβ”€β”€ Mock at interface boundaries only +β”œβ”€β”€ Benchmark before optimizing: go test -bench=. +β”œβ”€β”€ Fuzz test parsers and validators: go test -fuzz=. +└── Use t.Helper() in test helper functions +``` + +## Test Organization + +``` +Where to put tests: +β”œβ”€β”€ Unit tests: same package (foo_test.go) +β”œβ”€β”€ Black-box tests: package foo_test (external perspective) +β”œβ”€β”€ Integration tests: //go:build integration tag +β”œβ”€β”€ Test fixtures: testdata/ directory (auto-ignored by go build) +└── Test helpers: internal/testutil/ package +``` + +## Mocking Strategy + +``` +β”œβ”€β”€ Define interfaces at consumer side +β”œβ”€β”€ Generate mocks: mockgen, moq, or hand-written +β”œβ”€β”€ Prefer hand-written fakes for simple interfaces +β”œβ”€β”€ Use mockgen for complex interfaces (many methods) +β”œβ”€β”€ NEVER mock what you don't own (wrap external deps first) +└── Integration test > mocking when feasible +```