agentic-team-templates 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ # Go Interfaces and Type Design
2
+
3
+ Go's type system is intentionally simple. Interfaces are implicit. Composition replaces inheritance. The goal is clarity, not cleverness.
4
+
5
+ ## Interface Design
6
+
7
+ ### Keep Interfaces Small
8
+
9
+ ```go
10
+ // Good: Single-method interfaces are the most powerful abstraction in Go
11
+ type Reader interface {
12
+ Read(p []byte) (n int, err error)
13
+ }
14
+
15
+ type Writer interface {
16
+ Write(p []byte) (n int, err error)
17
+ }
18
+
19
+ // Compose small interfaces into larger ones when needed
20
+ type ReadWriter interface {
21
+ Reader
22
+ Writer
23
+ }
24
+
25
+ // Bad: Large interfaces that force implementers to satisfy methods they don't need
26
+ type Repository interface {
27
+ Create(ctx context.Context, entity Entity) error
28
+ GetByID(ctx context.Context, id string) (*Entity, error)
29
+ Update(ctx context.Context, entity Entity) error
30
+ Delete(ctx context.Context, id string) error
31
+ List(ctx context.Context, filter Filter) ([]Entity, error)
32
+ Count(ctx context.Context, filter Filter) (int, error)
33
+ // 6 methods — any mock must implement all 6
34
+ }
35
+
36
+ // Better: Separate interfaces by consumer need
37
+ type EntityReader interface {
38
+ GetByID(ctx context.Context, id string) (*Entity, error)
39
+ }
40
+
41
+ type EntityWriter interface {
42
+ Create(ctx context.Context, entity Entity) error
43
+ Update(ctx context.Context, entity Entity) error
44
+ }
45
+ ```
46
+
47
+ ### Define Interfaces at the Consumer, Not the Producer
48
+
49
+ ```go
50
+ // Good: The consumer defines what it needs
51
+ // In package "handler":
52
+ type UserFinder interface {
53
+ FindByID(ctx context.Context, id string) (*User, error)
54
+ }
55
+
56
+ type Handler struct {
57
+ users UserFinder // Only needs one method
58
+ }
59
+
60
+ // In package "postgres":
61
+ // *UserRepo satisfies handler.UserFinder implicitly
62
+ type UserRepo struct { db *sql.DB }
63
+
64
+ func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) { ... }
65
+ func (r *UserRepo) Create(ctx context.Context, u *User) error { ... }
66
+ func (r *UserRepo) Delete(ctx context.Context, id string) error { ... }
67
+
68
+ // Bad: Producer defines an interface and forces consumers to accept all of it
69
+ // package "postgres"
70
+ type UserRepository interface {
71
+ FindByID(ctx context.Context, id string) (*User, error)
72
+ Create(ctx context.Context, u *User) error
73
+ Delete(ctx context.Context, id string) error
74
+ }
75
+ ```
76
+
77
+ ### Interface Naming
78
+
79
+ ```go
80
+ // Single-method interfaces: method name + "er"
81
+ type Reader interface { Read(p []byte) (int, error) }
82
+ type Closer interface { Close() error }
83
+ type Stringer interface { String() string }
84
+ type Handler interface { ServeHTTP(ResponseWriter, *Request) }
85
+
86
+ // Multi-method interfaces: descriptive noun
87
+ type ReadCloser interface {
88
+ Reader
89
+ Closer
90
+ }
91
+ ```
92
+
93
+ ## Struct Design
94
+
95
+ ### Functional Options
96
+
97
+ ```go
98
+ // For constructors with many optional parameters
99
+ type Server struct {
100
+ addr string
101
+ readTimeout time.Duration
102
+ writeTimeout time.Duration
103
+ logger *slog.Logger
104
+ }
105
+
106
+ type Option func(*Server)
107
+
108
+ func WithReadTimeout(d time.Duration) Option {
109
+ return func(s *Server) { s.readTimeout = d }
110
+ }
111
+
112
+ func WithWriteTimeout(d time.Duration) Option {
113
+ return func(s *Server) { s.writeTimeout = d }
114
+ }
115
+
116
+ func WithLogger(l *slog.Logger) Option {
117
+ return func(s *Server) { s.logger = l }
118
+ }
119
+
120
+ func NewServer(addr string, opts ...Option) *Server {
121
+ s := &Server{
122
+ addr: addr,
123
+ readTimeout: 30 * time.Second, // Sensible defaults
124
+ writeTimeout: 30 * time.Second,
125
+ logger: slog.Default(),
126
+ }
127
+ for _, opt := range opts {
128
+ opt(s)
129
+ }
130
+ return s
131
+ }
132
+
133
+ // Usage
134
+ srv := NewServer(":8080",
135
+ WithReadTimeout(10*time.Second),
136
+ WithLogger(logger),
137
+ )
138
+ ```
139
+
140
+ ### Embedding for Composition
141
+
142
+ ```go
143
+ // Good: Embed to compose behavior
144
+ type LoggingMiddleware struct {
145
+ next http.Handler
146
+ logger *slog.Logger
147
+ }
148
+
149
+ // Good: Embed mutex to protect struct fields
150
+ type SafeMap struct {
151
+ mu sync.RWMutex
152
+ m map[string]string
153
+ }
154
+
155
+ // Bad: Embedding to "inherit" — Go doesn't have inheritance
156
+ type Animal struct { Name string }
157
+ type Dog struct {
158
+ Animal // This is composition, not inheritance — don't treat it as inheritance
159
+ }
160
+ ```
161
+
162
+ ### Method Receivers
163
+
164
+ ```go
165
+ // Use pointer receivers when:
166
+ // - The method mutates the receiver
167
+ // - The struct is large (avoid copying)
168
+ // - Consistency: if any method needs a pointer, use pointers for all
169
+ func (s *Server) Start() error { ... }
170
+
171
+ // Use value receivers when:
172
+ // - The struct is small and immutable (time.Time, etc.)
173
+ // - The method doesn't modify the receiver
174
+ func (p Point) Distance(other Point) float64 { ... }
175
+
176
+ // Never mix pointer and value receivers on the same type
177
+ // Pick one and be consistent
178
+ ```
179
+
180
+ ## Type Aliases and Definitions
181
+
182
+ ```go
183
+ // Type definitions create a new, distinct type
184
+ type UserID string
185
+ type Celsius float64
186
+
187
+ // Prevents mixing incompatible values
188
+ func GetUser(id UserID) (*User, error) { ... }
189
+ // GetUser("raw-string") won't compile — must be UserID("raw-string")
190
+
191
+ // Type aliases are for migration, not abstraction
192
+ type OldName = NewName // Both are the same type — useful for gradual refactors
193
+ ```
194
+
195
+ ## Generics (Go 1.18+)
196
+
197
+ ```go
198
+ // Use generics when the logic is identical across types
199
+ // and the type parameter adds real value
200
+
201
+ // Good: Generic data structures and algorithms
202
+ func Map[T, U any](s []T, f func(T) U) []U {
203
+ result := make([]U, len(s))
204
+ for i, v := range s {
205
+ result[i] = f(v)
206
+ }
207
+ return result
208
+ }
209
+
210
+ func Filter[T any](s []T, predicate func(T) bool) []T {
211
+ var result []T
212
+ for _, v := range s {
213
+ if predicate(v) {
214
+ result = append(result, v)
215
+ }
216
+ }
217
+ return result
218
+ }
219
+
220
+ // Good: Type constraints for compile-time safety
221
+ type Ordered interface {
222
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
223
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
224
+ ~float32 | ~float64 | ~string
225
+ }
226
+
227
+ func Max[T Ordered](a, b T) T {
228
+ if a > b {
229
+ return a
230
+ }
231
+ return b
232
+ }
233
+
234
+ // Bad: Generics for the sake of generics
235
+ // If there's only one concrete type, don't make it generic
236
+ // If an interface works, prefer the interface
237
+ ```
238
+
239
+ ## Anti-Patterns
240
+
241
+ ```go
242
+ // Never: Empty interface (any) as a parameter when the type is known
243
+ func Process(data any) { ... } // What is data? No one knows without reading the implementation.
244
+
245
+ // Never: Interface pollution — defining interfaces you don't consume
246
+ // If only one type implements the interface, you probably don't need the interface
247
+
248
+ // Never: Returning interfaces from constructors (hides information)
249
+ func New() MyInterface { return &myImpl{} } // BAD — return *myImpl
250
+
251
+ // Never: Stuttering — the package name is part of the identifier
252
+ package user
253
+ type UserService struct{} // user.UserService stutters
254
+ type Service struct{} // user.Service reads cleanly
255
+ ```
@@ -0,0 +1,139 @@
1
+ # Go Expert
2
+
3
+ Guidelines for principal-level Go engineering. Idiomatic Go, production systems, and deep runtime knowledge.
4
+
5
+ ## Scope
6
+
7
+ This ruleset applies to:
8
+ - HTTP services and gRPC servers
9
+ - CLI tools and system utilities
10
+ - Libraries and shared packages
11
+ - Distributed systems and microservices
12
+ - Data pipelines and stream processing
13
+ - Infrastructure tooling and platform code
14
+
15
+ ## Core Philosophy
16
+
17
+ Go is a language of restraint. The best Go code is boring Go code.
18
+
19
+ - **Simplicity is the highest virtue.** If a junior engineer can't read it, it's too clever.
20
+ - **The standard library is your first dependency.** Reach for third-party packages only when the stdlib genuinely falls short.
21
+ - **Errors are values, not exceptions.** Handle them explicitly at every call site.
22
+ - **Concurrency is a tool, not a default.** Don't use goroutines because you can — use them because the problem demands it.
23
+ - **Interfaces are discovered, not designed.** Accept interfaces, return structs.
24
+ - **If you don't know, say you don't know.** Guessing at behavior you haven't verified is worse than admitting uncertainty.
25
+
26
+ ## Key Principles
27
+
28
+ ### 1. Effective Go Is the Baseline
29
+
30
+ The official [Effective Go](https://go.dev/doc/effective_go) document and the [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) wiki are not suggestions — they are the floor. Everything in this guide builds on top of those foundations.
31
+
32
+ ### 2. Package Design
33
+
34
+ ```go
35
+ // A package should have a single, clear purpose.
36
+ // The package name IS the documentation.
37
+
38
+ // Good: package names that describe what they do
39
+ package httputil // HTTP utility functions
40
+ package authz // Authorization logic
41
+ package sqlmigrate // SQL migration runner
42
+
43
+ // Bad: package names that describe nothing
44
+ package util // Util of what?
45
+ package common // Common to whom?
46
+ package helpers // Helping with what?
47
+ package base // Base of what?
48
+ ```
49
+
50
+ ### 3. Accept Interfaces, Return Structs
51
+
52
+ ```go
53
+ // Good: Accept the narrowest interface that satisfies the need
54
+ func ProcessRecords(r io.Reader) error { ... }
55
+
56
+ // Good: Return concrete types — let callers decide abstraction
57
+ func NewServer(cfg Config) *Server { ... }
58
+
59
+ // Bad: Returning an interface hides information and prevents extension
60
+ func NewServer(cfg Config) ServerInterface { ... }
61
+
62
+ // Bad: Accepting a concrete type when only one method is needed
63
+ func ProcessRecords(f *os.File) error { ... }
64
+ ```
65
+
66
+ ### 4. Zero Values Are Useful
67
+
68
+ ```go
69
+ // Design types so zero values are valid and usable
70
+ type Buffer struct {
71
+ buf []byte
72
+ // No constructor needed — zero value is an empty, ready-to-use buffer
73
+ }
74
+
75
+ // sync.Mutex zero value is an unlocked mutex — no Init() needed
76
+ var mu sync.Mutex
77
+
78
+ // bytes.Buffer zero value is an empty buffer ready for writes
79
+ var buf bytes.Buffer
80
+ buf.WriteString("hello")
81
+ ```
82
+
83
+ ### 5. Error Handling Is Not Boilerplate
84
+
85
+ ```go
86
+ // Every error check is a conscious decision about what happens next.
87
+ // If you find yourself writing the same error handling repeatedly,
88
+ // the API design needs work — not the error handling.
89
+
90
+ // Good: Add context at every level
91
+ result, err := db.QueryContext(ctx, query, args...)
92
+ if err != nil {
93
+ return fmt.Errorf("querying users by email %q: %w", email, err)
94
+ }
95
+ ```
96
+
97
+ ## Project Structure
98
+
99
+ ```
100
+ project/
101
+ ├── cmd/ # Main applications
102
+ │ └── myapp/
103
+ │ └── main.go # Minimal — wiring only
104
+ ├── internal/ # Private application code
105
+ │ ├── domain/ # Core business types and logic
106
+ │ ├── service/ # Application services / use cases
107
+ │ ├── repository/ # Data access implementations
108
+ │ ├── handler/ # HTTP/gRPC handlers
109
+ │ └── platform/ # Infrastructure concerns (logging, metrics, config)
110
+ ├── pkg/ # Public library code (use sparingly)
111
+ ├── api/ # API definitions (OpenAPI, protobuf)
112
+ ├── migrations/ # Database migrations
113
+ ├── testdata/ # Test fixtures
114
+ ├── go.mod
115
+ ├── go.sum
116
+ └── Makefile
117
+ ```
118
+
119
+ ### Layout Rules
120
+
121
+ - `cmd/` contains only `main.go` files that wire dependencies and call `run()`
122
+ - `internal/` is your private code — the compiler enforces this boundary
123
+ - `pkg/` is for genuinely reusable library code intended for other projects — most projects don't need it
124
+ - Never put business logic in `cmd/` or `handler/`
125
+ - Co-locate tests with the code they test: `foo.go` and `foo_test.go` in the same package
126
+
127
+ ## Definition of Done
128
+
129
+ A Go feature is complete when:
130
+ - [ ] `go vet ./...` reports zero issues
131
+ - [ ] `staticcheck ./...` reports zero issues
132
+ - [ ] `golangci-lint run` passes with the project's config
133
+ - [ ] `go test -race ./...` passes with no race conditions
134
+ - [ ] Test coverage covers meaningful behavior (not just lines)
135
+ - [ ] Error paths are tested, not just happy paths
136
+ - [ ] Documentation comments on all exported identifiers
137
+ - [ ] No `TODO` or `FIXME` without an associated issue
138
+ - [ ] Context propagation is correct (no `context.Background()` in request paths)
139
+ - [ ] Resource cleanup verified (deferred closes, connection pools)
@@ -0,0 +1,234 @@
1
+ # Go Performance
2
+
3
+ Go is fast by default. The garbage collector, runtime scheduler, and compiler do heavy lifting. Your job is to not get in the way — and to know how to profile when something is slow.
4
+
5
+ ## Profile First
6
+
7
+ Never optimize without profiling data. Intuition about bottlenecks is wrong more often than it's right.
8
+
9
+ ```go
10
+ // CPU profiling
11
+ import "runtime/pprof"
12
+
13
+ f, _ := os.Create("cpu.prof")
14
+ pprof.StartCPUProfile(f)
15
+ defer pprof.StopCPUProfile()
16
+
17
+ // Memory profiling
18
+ f, _ := os.Create("mem.prof")
19
+ pprof.WriteHeapProfile(f)
20
+
21
+ // In HTTP servers — import for side effects
22
+ import _ "net/http/pprof"
23
+ // Then: go tool pprof http://localhost:6060/debug/pprof/heap
24
+
25
+ // Analyze
26
+ // go tool pprof cpu.prof
27
+ // (pprof) top10
28
+ // (pprof) web # Opens in browser
29
+ // (pprof) list FunctionName
30
+ ```
31
+
32
+ ## Memory Allocation
33
+
34
+ ### Preallocate Slices
35
+
36
+ ```go
37
+ // Good: Preallocate when length is known
38
+ users := make([]User, 0, len(ids))
39
+ for _, id := range ids {
40
+ u, err := getUser(id)
41
+ if err != nil {
42
+ return nil, err
43
+ }
44
+ users = append(users, u)
45
+ }
46
+
47
+ // Bad: Growing slice dynamically
48
+ var users []User // Repeated reallocation as it grows
49
+ ```
50
+
51
+ ### Avoid Unnecessary Allocations
52
+
53
+ ```go
54
+ // Good: Reuse buffers
55
+ var bufPool = sync.Pool{
56
+ New: func() any { return new(bytes.Buffer) },
57
+ }
58
+
59
+ func process(data []byte) string {
60
+ buf := bufPool.Get().(*bytes.Buffer)
61
+ defer func() {
62
+ buf.Reset()
63
+ bufPool.Put(buf)
64
+ }()
65
+
66
+ buf.Write(data)
67
+ // ... process ...
68
+ return buf.String()
69
+ }
70
+
71
+ // Good: strings.Builder for string concatenation
72
+ var b strings.Builder
73
+ for _, s := range parts {
74
+ b.WriteString(s)
75
+ }
76
+ result := b.String()
77
+
78
+ // Bad: String concatenation in a loop
79
+ result := ""
80
+ for _, s := range parts {
81
+ result += s // Allocates a new string every iteration
82
+ }
83
+ ```
84
+
85
+ ### Pointer vs Value Semantics
86
+
87
+ ```go
88
+ // Small structs (< ~64 bytes): pass by value — avoids heap allocation
89
+ type Point struct{ X, Y float64 }
90
+ func Distance(a, b Point) float64 { ... } // Copies are cheap, stays on stack
91
+
92
+ // Large structs or structs that must be shared: pass by pointer
93
+ type Config struct { /* many fields */ }
94
+ func NewServer(cfg *Config) *Server { ... }
95
+
96
+ // Slices, maps, and channels are already reference types — don't pass pointers to them
97
+ func Process(items []Item) { ... } // Good
98
+ func Process(items *[]Item) { ... } // Bad (unless you need to modify the slice header)
99
+ ```
100
+
101
+ ## Concurrency Performance
102
+
103
+ ### Minimize Lock Contention
104
+
105
+ ```go
106
+ // Use sync.RWMutex for read-heavy workloads
107
+ type Cache struct {
108
+ mu sync.RWMutex
109
+ data map[string]Entry
110
+ }
111
+
112
+ func (c *Cache) Get(key string) (Entry, bool) {
113
+ c.mu.RLock()
114
+ defer c.mu.RUnlock()
115
+ e, ok := c.data[key]
116
+ return e, ok
117
+ }
118
+
119
+ func (c *Cache) Set(key string, entry Entry) {
120
+ c.mu.Lock()
121
+ defer c.mu.Unlock()
122
+ c.data[key] = entry
123
+ }
124
+
125
+ // For high-contention counters, use atomic operations
126
+ var requestCount atomic.Int64
127
+ requestCount.Add(1)
128
+ ```
129
+
130
+ ### Avoid Goroutine Leaks
131
+
132
+ ```go
133
+ // Every goroutine must have a clear exit condition
134
+ // Use context cancellation to signal shutdown
135
+
136
+ func startWorkers(ctx context.Context, n int) {
137
+ var wg sync.WaitGroup
138
+ for range n {
139
+ wg.Add(1)
140
+ go func() {
141
+ defer wg.Done()
142
+ for {
143
+ select {
144
+ case <-ctx.Done():
145
+ return
146
+ default:
147
+ doWork()
148
+ }
149
+ }
150
+ }()
151
+ }
152
+ wg.Wait()
153
+ }
154
+ ```
155
+
156
+ ## Compiler Optimizations
157
+
158
+ ```go
159
+ // Inlining: Small functions are automatically inlined
160
+ // Check: go build -gcflags="-m" ./...
161
+
162
+ // Escape analysis: The compiler decides stack vs heap
163
+ // Check: go build -gcflags="-m" ./...
164
+ // "moved to heap" = allocation — consider if avoidable
165
+
166
+ // Bounds check elimination: The compiler removes bounds checks
167
+ // when it can prove safety
168
+ s := data[0:10]
169
+ for i := range s { // Compiler knows i is in bounds — no check needed
170
+ process(s[i])
171
+ }
172
+ ```
173
+
174
+ ## Database Performance
175
+
176
+ ```go
177
+ // Always use connection pooling
178
+ db, err := sql.Open("postgres", connString)
179
+ db.SetMaxOpenConns(25) // Limit concurrent connections
180
+ db.SetMaxIdleConns(5) // Keep some connections warm
181
+ db.SetConnMaxLifetime(5 * time.Minute) // Rotate connections
182
+
183
+ // Use prepared statements for repeated queries
184
+ stmt, err := db.PrepareContext(ctx, "SELECT * FROM users WHERE id = $1")
185
+ defer stmt.Close()
186
+
187
+ // Use batch operations for bulk inserts
188
+ // Use COPY protocol (pgx) for large data loads
189
+ ```
190
+
191
+ ## Benchmarking Best Practices
192
+
193
+ ```go
194
+ func BenchmarkJSON(b *testing.B) {
195
+ data := loadTestData()
196
+
197
+ b.ResetTimer() // Exclude setup time
198
+ b.ReportAllocs() // Report memory allocations
199
+
200
+ for b.Loop() {
201
+ var result MyStruct
202
+ if err := json.Unmarshal(data, &result); err != nil {
203
+ b.Fatal(err)
204
+ }
205
+ }
206
+ }
207
+
208
+ // Compare implementations
209
+ func BenchmarkJSON_StdLib(b *testing.B) { ... }
210
+ func BenchmarkJSON_Sonic(b *testing.B) { ... }
211
+
212
+ // Run: go test -bench=. -benchmem -count=5 ./...
213
+ // Use benchstat to compare: benchstat old.txt new.txt
214
+ ```
215
+
216
+ ## Anti-Patterns
217
+
218
+ ```go
219
+ // Never: Premature optimization
220
+ // Profile first. The bottleneck is almost never where you think it is.
221
+
222
+ // Never: sync.Pool for small, short-lived objects
223
+ // The GC handles these efficiently. Pool is for large buffers.
224
+
225
+ // Never: Manual memory management tricks
226
+ // Go's GC is highly optimized. Fight the runtime and you'll lose.
227
+
228
+ // Never: Caching without bounds
229
+ cache := make(map[string]Result) // Grows forever — OOM eventually
230
+ // Use an LRU cache with a max size.
231
+
232
+ // Never: Blocking operations in hot paths without timeouts
233
+ resp, err := http.Get(url) // No timeout! Use http.Client with Timeout set.
234
+ ```