oh-my-customcode 0.36.2 → 0.37.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.
Files changed (61) hide show
  1. package/dist/cli/index.js +47 -2
  2. package/dist/index.js +44 -0
  3. package/package.json +1 -1
  4. package/templates/.claude/agents/arch-documenter.md +4 -1
  5. package/templates/.claude/agents/arch-speckit-agent.md +15 -0
  6. package/templates/.claude/agents/be-django-expert.md +1 -0
  7. package/templates/.claude/agents/be-express-expert.md +1 -0
  8. package/templates/.claude/agents/be-fastapi-expert.md +1 -0
  9. package/templates/.claude/agents/be-go-backend-expert.md +1 -0
  10. package/templates/.claude/agents/be-nestjs-expert.md +1 -0
  11. package/templates/.claude/agents/be-springboot-expert.md +1 -0
  12. package/templates/.claude/agents/db-postgres-expert.md +1 -0
  13. package/templates/.claude/agents/db-redis-expert.md +1 -0
  14. package/templates/.claude/agents/db-supabase-expert.md +1 -0
  15. package/templates/.claude/agents/de-airflow-expert.md +1 -0
  16. package/templates/.claude/agents/de-dbt-expert.md +1 -0
  17. package/templates/.claude/agents/de-kafka-expert.md +1 -0
  18. package/templates/.claude/agents/de-pipeline-expert.md +1 -0
  19. package/templates/.claude/agents/de-snowflake-expert.md +1 -0
  20. package/templates/.claude/agents/de-spark-expert.md +1 -0
  21. package/templates/.claude/agents/fe-flutter-agent.md +1 -0
  22. package/templates/.claude/agents/fe-svelte-agent.md +1 -0
  23. package/templates/.claude/agents/fe-vercel-agent.md +1 -0
  24. package/templates/.claude/agents/fe-vuejs-agent.md +1 -0
  25. package/templates/.claude/agents/infra-aws-expert.md +1 -0
  26. package/templates/.claude/agents/infra-docker-expert.md +1 -0
  27. package/templates/.claude/agents/lang-golang-expert.md +1 -0
  28. package/templates/.claude/agents/lang-java21-expert.md +3 -0
  29. package/templates/.claude/agents/lang-kotlin-expert.md +1 -0
  30. package/templates/.claude/agents/lang-python-expert.md +1 -0
  31. package/templates/.claude/agents/lang-rust-expert.md +1 -0
  32. package/templates/.claude/agents/lang-typescript-expert.md +1 -0
  33. package/templates/.claude/agents/mgr-claude-code-bible.md +1 -2
  34. package/templates/.claude/agents/mgr-creator.md +1 -0
  35. package/templates/.claude/agents/mgr-gitnerd.md +1 -0
  36. package/templates/.claude/agents/mgr-sauron.md +5 -2
  37. package/templates/.claude/agents/mgr-supplier.md +1 -3
  38. package/templates/.claude/agents/mgr-updater.md +1 -0
  39. package/templates/.claude/agents/qa-engineer.md +1 -0
  40. package/templates/.claude/agents/qa-planner.md +4 -1
  41. package/templates/.claude/agents/qa-writer.md +1 -1
  42. package/templates/.claude/agents/sec-codeql-expert.md +4 -2
  43. package/templates/.claude/agents/sys-memory-keeper.md +30 -0
  44. package/templates/.claude/agents/sys-naggy.md +36 -2
  45. package/templates/.claude/agents/tool-bun-expert.md +1 -1
  46. package/templates/.claude/agents/tool-npm-expert.md +1 -1
  47. package/templates/.claude/agents/tool-optimizer.md +1 -2
  48. package/templates/.claude/hooks/hooks.json +2 -2
  49. package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +10 -0
  50. package/templates/.claude/hooks/scripts/content-hash-validator.sh +2 -3
  51. package/templates/.claude/hooks/scripts/schema-validator.sh +15 -0
  52. package/templates/.claude/hooks/scripts/secret-filter.sh +31 -1
  53. package/templates/.claude/rules/MUST-agent-teams.md +0 -23
  54. package/templates/.claude/rules/MUST-orchestrator-coordination.md +1 -13
  55. package/templates/.claude/skills/django-best-practices/SKILL.md +27 -134
  56. package/templates/.claude/skills/flutter-best-practices/SKILL.md +39 -146
  57. package/templates/.claude/skills/go-backend-best-practices/SKILL.md +29 -233
  58. package/templates/.claude/skills/java21-best-practices/SKILL.md +48 -163
  59. package/templates/CLAUDE.md.en +7 -65
  60. package/templates/CLAUDE.md.ko +7 -65
  61. package/templates/manifest.json +1 -1
@@ -46,15 +46,7 @@ state_choice:
46
46
 
47
47
  build_context:
48
48
  rule: "Never store BuildContext across async gaps"
49
- pattern: |
50
- // BAD
51
- final ctx = context;
52
- await Future.delayed(Duration(seconds: 1));
53
- Navigator.of(ctx).push(...); // context may be invalid
54
-
55
- // GOOD
56
- if (!mounted) return;
57
- Navigator.of(context).push(...);
49
+ pattern: "Check mounted before using context after await"
58
50
 
59
51
  keys:
60
52
  ValueKey: "When items have unique business identity"
@@ -83,6 +75,8 @@ slivers:
83
75
  avoid: "Nested ListView in ListView"
84
76
  ```
85
77
 
78
+ Reference: guides/flutter/fundamentals.md
79
+
86
80
  ### 2. State Management
87
81
 
88
82
  ```yaml
@@ -99,51 +93,26 @@ riverpod_patterns:
99
93
  async_state: "AsyncNotifier + AsyncValue (loading/data/error)"
100
94
  family: "family modifier for parameterized providers"
101
95
  keep_alive: "Only when justified (expensive computations)"
102
- invalidate_vs_refresh: |
103
- ref.invalidate(provider) // reset to loading, lazy re-fetch
104
- ref.refresh(provider) // immediate re-fetch, return new value
96
+ invalidate_vs_refresh: "ref.invalidate() resets to loading (lazy); ref.refresh() immediate re-fetch"
105
97
 
106
98
  bloc_patterns:
107
99
  one_event_per_action: "One UI action = one event class"
108
- cubit_vs_bloc: |
109
- Cubit: direct emit(state) — for simple state changes
110
- Bloc: event → state mapping — when audit trail needed
100
+ cubit_vs_bloc: "Cubit for simple state changes; Bloc when audit trail needed"
111
101
  never: "Emit state in constructor body"
112
- listener_vs_consumer: |
113
- BlocListener: side effects (navigation, snackbar)
114
- BlocConsumer: rebuild UI + side effects
115
- BlocBuilder: rebuild UI only (most common)
102
+ listener_vs_consumer: "BlocListener for side effects; BlocConsumer for UI + effects; BlocBuilder for UI only"
116
103
  stream_management: "Cancel subscriptions in close()"
117
104
 
118
105
  state_immutability:
119
106
  rule: "All state objects must be immutable"
120
107
  tool: "freezed package for copyWith/==/hashCode generation"
121
- pattern: |
122
- @freezed
123
- class UserState with _$UserState {
124
- const factory UserState({
125
- required String name,
126
- required int age,
127
- @Default(false) bool isLoading,
128
- }) = _UserState;
129
- }
130
108
 
131
109
  result_type:
132
110
  rule: "Return Result<T> from repositories, never throw"
133
- pattern: |
134
- sealed class Result<T> {
135
- const Result();
136
- }
137
- final class Ok<T> extends Result<T> {
138
- const Ok(this.value);
139
- final T value;
140
- }
141
- final class Error<T> extends Result<T> {
142
- const Error(this.error);
143
- final Exception error;
144
- }
111
+ pattern: "sealed class Result<T> with Ok<T> and Error<T> subclasses"
145
112
  ```
146
113
 
114
+ Reference: guides/flutter/state-management.md
115
+
147
116
  ### 3. Performance
148
117
 
149
118
  ```yaml
@@ -151,16 +120,7 @@ build_optimization:
151
120
  const_widgets: "Mark immutable widgets const — zero rebuild"
152
121
  localize_setState: "Call setState on smallest possible subtree"
153
122
  extract_widgets: "StatelessWidget class > helper method"
154
- child_parameter: |
155
- // GOOD: static child passed through
156
- AnimatedBuilder(
157
- animation: controller,
158
- builder: (context, child) => Transform.rotate(
159
- angle: controller.value,
160
- child: child, // not rebuilt
161
- ),
162
- child: const ExpensiveWidget(), // built once
163
- )
123
+ child_parameter: "Pass static child through AnimatedBuilder to avoid rebuild"
164
124
 
165
125
  rebuild_avoidance:
166
126
  consumer_placement: "Place Consumer/ListenableBuilder as deep as possible"
@@ -183,6 +143,8 @@ frame_budget:
183
143
  tool: "DevTools Performance view for jank detection"
184
144
  ```
185
145
 
146
+ Reference: guides/flutter/performance.md
147
+
186
148
  ### 4. Testing
187
149
 
188
150
  ```yaml
@@ -192,36 +154,16 @@ test_pyramid:
192
154
  integration: "Full app on device — slow, high confidence"
193
155
  golden: "Visual regression via matchesGoldenFile()"
194
156
 
195
- widget_test_pattern: |
196
- testWidgets('shows loading then content', (tester) async {
197
- await tester.pumpWidget(
198
- ProviderScope(
199
- overrides: [productsProvider.overrideWith((_) => fakeProducts)],
200
- child: const MaterialApp(home: ProductListScreen()),
201
- ),
202
- );
203
- expect(find.byType(CircularProgressIndicator), findsOneWidget);
204
- await tester.pumpAndSettle();
205
- expect(find.byType(ProductCard), findsNWidgets(3));
206
- });
157
+ widget_test_pattern:
158
+ rule: "Use pumpWidget with ProviderScope overrides, then pump/pumpAndSettle for async"
207
159
 
208
160
  mocking:
209
161
  prefer: "mocktail (null-safe, no codegen)"
210
162
  avoid: "Legacy mockito with build_runner"
211
163
  fakes: "Use Fake implementations for deterministic tests"
212
- pattern: |
213
- class FakeProductRepository extends Fake implements ProductRepository {
214
- @override
215
- Future<Result<List<Product>>> getAll() async => Ok(testProducts);
216
- }
217
-
218
- bloc_testing: |
219
- blocTest<AuthBloc, AuthState>(
220
- 'emits [loading, success] when login succeeds',
221
- build: () => AuthBloc(FakeAuthRepository()),
222
- act: (bloc) => bloc.add(LoginRequested('user', 'pass')),
223
- expect: () => [AuthLoading(), isA<AuthSuccess>()],
224
- );
164
+
165
+ bloc_testing:
166
+ rule: "Use blocTest<Bloc, State> with build/act/expect pattern"
225
167
 
226
168
  coverage_target:
227
169
  widget_tests: "80%+ for UI logic"
@@ -229,6 +171,8 @@ coverage_target:
229
171
  integration: "Critical user flows only"
230
172
  ```
231
173
 
174
+ Reference: guides/flutter/testing.md
175
+
232
176
  ### 5. Security
233
177
 
234
178
  ```yaml
@@ -236,14 +180,14 @@ secrets:
236
180
  never: "Hardcode API keys, tokens, or credentials in source"
237
181
  best: "Backend proxy for all sensitive API calls"
238
182
  use: "--dart-define-from-file=.env for NON-SECRET build config only (feature flags, environment URLs)"
239
- warning: "dart-define values are embedded in compiled binary and extractable via static analysis. Use only for non-secret build configuration (feature flags, environment URLs)."
183
+ warning: "dart-define values are embedded in compiled binary and extractable via static analysis"
240
184
 
241
185
  storage:
242
186
  sensitive_data: "flutter_secure_storage v10+ (Keychain/Keystore)"
243
187
  never: "SharedPreferences for tokens, PII, or credentials"
244
188
  ios: "AppleOptions(useSecureEnclave: true) for high-value"
245
189
  android: "AndroidOptions(encryptedSharedPreferences: true)"
246
- web_warning: "flutter_secure_storage on Web uses localStorage by default, which is accessible to any JavaScript on the page (XSS vulnerable). For Web targets, use HttpOnly cookies managed by backend or in-memory-only storage for sensitive data."
190
+ web_warning: "flutter_secure_storage on Web uses localStorage by default (XSS vulnerable). Use HttpOnly cookies or in-memory-only for sensitive data."
247
191
 
248
192
  network:
249
193
  tls: "Certificate pinning (SPKI) for financial/health apps"
@@ -267,54 +211,35 @@ logging:
267
211
  never: "Log PII, tokens, or credentials"
268
212
  ```
269
213
 
214
+ Reference: guides/flutter/security.md
215
+
270
216
  ### 6. Dart Language Patterns
271
217
 
272
218
  ```yaml
273
219
  naming:
274
- types: "UpperCamelCase for classes, enums, typedefs, extensions, mixins (e.g., HttpClient, JsonParser)"
275
- variables: "lowerCamelCase for variables, parameters, named constants (e.g., itemCount, defaultTimeout)"
276
- libraries: "lowercase_with_underscores for libraries, packages, directories, source files (e.g., my_package, slider_menu.dart)"
277
- constants: "lowerCamelCase for const (e.g., const defaultTimeout = 30), NOT SCREAMING_CAPS"
278
- private: "Prefix with underscore for library-private (e.g., _internalCache, _helper())"
279
- boolean: "Prefix with is/has/can/should for booleans (e.g., isEnabled, hasData, canScroll)"
280
- avoid: "Hungarian notation, type prefixes (strName, lstItems), abbreviations unless universally known (e.g., ok: http, url, id; avoid: mgr, ctx, btn)"
220
+ types: "UpperCamelCase for classes, enums, typedefs, extensions, mixins"
221
+ variables: "lowerCamelCase for variables, parameters, named constants"
222
+ libraries: "lowercase_with_underscores for libraries, packages, directories, source files"
223
+ constants: "lowerCamelCase for const (NOT SCREAMING_CAPS)"
224
+ private: "Prefix with underscore for library-private"
225
+ boolean: "Prefix with is/has/can/should"
226
+ avoid: "Hungarian notation, type prefixes, abbreviations unless universally known"
281
227
 
282
228
  null_safety:
283
229
  default: "Non-nullable types — use ? only when null is meaningful"
284
230
  avoid_bang: "Minimize ! operator — use only when null is logically impossible"
285
231
  late: "Only when initialization is guaranteed before use"
286
- pattern: |
287
- // GOOD
288
- final name = user?.name ?? 'Anonymous';
289
-
290
- // AVOID
291
- final name = user!.name; // crashes if null
292
232
 
293
233
  sealed_classes:
294
234
  use_for: "Exhaustive pattern matching on state/result types"
295
- pattern: |
296
- sealed class AuthState {}
297
- class AuthInitial extends AuthState {}
298
- class AuthLoading extends AuthState {}
299
- class AuthSuccess extends AuthState { final User user; AuthSuccess(this.user); }
300
- class AuthError extends AuthState { final String message; AuthError(this.message); }
301
-
302
- // Exhaustive switch — compiler enforces all cases
303
- return switch (state) {
304
- AuthInitial() => LoginScreen(),
305
- AuthLoading() => CircularProgressIndicator(),
306
- AuthSuccess(:final user) => HomeScreen(user: user),
307
- AuthError(:final message) => ErrorWidget(message),
308
- };
235
+ pattern: "sealed class with subclass per state, exhaustive switch expression"
309
236
 
310
237
  records:
311
238
  use_for: "Lightweight multi-value returns without class boilerplate"
312
- pattern: "(String name, int age) getUserInfo() => ('Alice', 30);"
313
239
  avoid: "Records for complex data — use freezed classes instead"
314
240
 
315
241
  extension_types:
316
242
  use_for: "Zero-cost type wrappers for primitive IDs"
317
- pattern: "extension type UserId(int id) implements int {}"
318
243
 
319
244
  immutability:
320
245
  prefer: "final variables, const constructors"
@@ -331,48 +256,19 @@ dynamic:
331
256
  reason: "No compile-time type checking, reduces IDE support"
332
257
  ```
333
258
 
259
+ Reference: guides/flutter/fundamentals.md
260
+
334
261
  ### 7. Architecture & Project Structure
335
262
 
336
263
  ```yaml
337
264
  default_structure:
338
- small_app: |
339
- lib/
340
- models/
341
- services/
342
- screens/
343
- widgets/
344
- medium_app: |
345
- lib/
346
- ui/
347
- core/themes/, core/widgets/
348
- <feature>/<feature>_screen.dart
349
- <feature>/<feature>_viewmodel.dart
350
- data/
351
- repositories/<entity>_repository.dart
352
- services/<source>_service.dart
353
- domain/ (optional)
354
- use_cases/
355
- large_app: |
356
- lib/
357
- core/ (shared)
358
- features/<feature>/
359
- data/datasources/, data/repositories/
360
- domain/entities/, domain/usecases/
361
- presentation/bloc/, presentation/pages/
265
+ small_app: "lib/{models,services,screens,widgets}/"
266
+ medium_app: "lib/{ui/{core/,<feature>/},data/{repositories/,services/},domain/}"
267
+ large_app: "lib/{core/,features/<feature>/{data/,domain/,presentation/}}"
362
268
 
363
269
  navigation:
364
270
  default: "go_router (official recommendation)"
365
- pattern: |
366
- GoRoute(
367
- path: '/product/:id',
368
- builder: (context, state) {
369
- final id = state.pathParameters['id']!;
370
- return ProductDetailScreen(id: id);
371
- },
372
- )
373
- go_vs_push: |
374
- context.go('/path') // replaces stack (navigation reset)
375
- context.push('/path') // adds to stack (back button works)
271
+ go_vs_push: "context.go() replaces stack; context.push() adds to stack"
376
272
 
377
273
  dependency_injection:
378
274
  riverpod: "Built-in — providers as DI (default)"
@@ -381,13 +277,12 @@ dependency_injection:
381
277
 
382
278
  environments:
383
279
  pattern: "Flavors + --dart-define for multi-environment builds"
384
- command: "flutter run --flavor development --target lib/main/main_development.dart"
385
280
  rule: "Separate bundle IDs, API URLs, and Firebase config per flavor"
386
281
  ```
387
282
 
388
- ## Default Stack
283
+ Reference: guides/flutter/architecture.md
389
284
 
390
- When starting a new Flutter project, recommend this stack:
285
+ ## Default Stack
391
286
 
392
287
  ```yaml
393
288
  state_management: Riverpod 3.0
@@ -402,8 +297,6 @@ structure: Official MVVM (lib/{ui,data}/)
402
297
 
403
298
  ## Enterprise Stack
404
299
 
405
- For regulated or large-team projects:
406
-
407
300
  ```yaml
408
301
  state_management: BLoC 9.0 + Cubit
409
302
  navigation: go_router or auto_route
@@ -14,25 +14,7 @@ Apply Go backend patterns for building production-ready services.
14
14
  ### 1. Project Structure (Standard Layout)
15
15
 
16
16
  ```yaml
17
- layout: |
18
- project/
19
- ├── cmd/
20
- │ └── server/
21
- │ └── main.go
22
- ├── internal/
23
- │ ├── handler/
24
- │ ├── service/
25
- │ ├── repository/
26
- │ └── model/
27
- ├── pkg/
28
- │ └── shared/
29
- ├── api/
30
- │ └── openapi.yaml
31
- ├── configs/
32
- ├── scripts/
33
- ├── Dockerfile
34
- ├── Makefile
35
- └── go.mod
17
+ layout: "cmd/{server/main.go} + internal/{handler/,service/,repository/,model/} + pkg/{shared/} + api/{openapi.yaml} + configs/ + scripts/"
36
18
 
37
19
  directories:
38
20
  cmd: Main applications (one per binary)
@@ -43,6 +25,8 @@ directories:
43
25
  scripts: Build and CI scripts
44
26
  ```
45
27
 
28
+ Reference: guides/go-backend/project-layout.md
29
+
46
30
  ### 2. Error Handling (Uber Style)
47
31
 
48
32
  ```yaml
@@ -52,28 +36,14 @@ principles:
52
36
  - Use sentinel errors for specific conditions
53
37
  - Name error variables with Err prefix
54
38
 
55
- patterns: |
56
- // Sentinel errors
57
- var (
58
- ErrNotFound = errors.New("not found")
59
- ErrInvalidInput = errors.New("invalid input")
60
- )
61
-
62
- // Wrap with context
63
- func getUser(id string) (*User, error) {
64
- user, err := db.FindUser(id)
65
- if err != nil {
66
- return nil, fmt.Errorf("getUser %s: %w", id, err)
67
- }
68
- return user, nil
69
- }
70
-
71
- // Check specific errors
72
- if errors.Is(err, ErrNotFound) {
73
- return http.StatusNotFound
74
- }
39
+ patterns:
40
+ sentinel: "var ErrNotFound = errors.New(\"not found\")"
41
+ wrapping: "fmt.Errorf(\"getUser %s: %w\", id, err)"
42
+ checking: "errors.Is(err, ErrNotFound)"
75
43
  ```
76
44
 
45
+ Reference: guides/go-backend/uber-style.md
46
+
77
47
  ### 3. Concurrency (Uber Style)
78
48
 
79
49
  ```yaml
@@ -84,44 +54,11 @@ channels:
84
54
  goroutines:
85
55
  never: fire-and-forget
86
56
  always: wait for completion or manage lifecycle
87
-
88
- patterns: |
89
- // Wait group for goroutines
90
- func process(items []Item) error {
91
- var wg sync.WaitGroup
92
- errCh := make(chan error, 1)
93
-
94
- for _, item := range items {
95
- wg.Add(1)
96
- go func(item Item) {
97
- defer wg.Done()
98
- if err := processItem(item); err != nil {
99
- select {
100
- case errCh <- err:
101
- default:
102
- }
103
- }
104
- }(item)
105
- }
106
-
107
- wg.Wait()
108
- close(errCh)
109
- return <-errCh
110
- }
111
-
112
- // Context for cancellation
113
- func longRunningTask(ctx context.Context) error {
114
- for {
115
- select {
116
- case <-ctx.Done():
117
- return ctx.Err()
118
- default:
119
- // do work
120
- }
121
- }
122
- }
57
+ patterns: "sync.WaitGroup + error channel for parallel work; context.Context for cancellation"
123
58
  ```
124
59
 
60
+ Reference: guides/go-backend/uber-style.md
61
+
125
62
  ### 4. HTTP Server
126
63
 
127
64
  ```yaml
@@ -130,88 +67,24 @@ structure:
130
67
  service: Business logic
131
68
  repository: Data access
132
69
 
133
- patterns: |
134
- // Handler with dependency injection
135
- type UserHandler struct {
136
- service UserService
137
- }
138
-
139
- func NewUserHandler(s UserService) *UserHandler {
140
- return &UserHandler{service: s}
141
- }
142
-
143
- func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
144
- id := chi.URLParam(r, "id")
145
-
146
- user, err := h.service.GetUser(r.Context(), id)
147
- if err != nil {
148
- if errors.Is(err, ErrNotFound) {
149
- http.Error(w, "user not found", http.StatusNotFound)
150
- return
151
- }
152
- http.Error(w, "internal error", http.StatusInternalServerError)
153
- return
154
- }
155
-
156
- json.NewEncoder(w).Encode(user)
157
- }
158
-
159
- // Router setup
160
- func NewRouter(h *UserHandler) *chi.Mux {
161
- r := chi.NewRouter()
162
- r.Use(middleware.Logger)
163
- r.Use(middleware.Recoverer)
164
-
165
- r.Route("/api/v1", func(r chi.Router) {
166
- r.Get("/users/{id}", h.GetUser)
167
- r.Post("/users", h.CreateUser)
168
- })
169
-
170
- return r
171
- }
70
+ patterns:
71
+ dependency_injection: "Handler struct with service field, constructor NewXxxHandler()"
72
+ router: "chi.NewRouter() with middleware (Logger, Recoverer) and versioned routes"
73
+ error_mapping: "errors.Is() to map domain errors to HTTP status codes"
172
74
  ```
173
75
 
76
+ Reference: guides/go-backend/uber-style.md
77
+
174
78
  ### 5. Dependency Injection
175
79
 
176
80
  ```yaml
177
81
  approach: constructor injection
178
82
  avoid: global variables
179
-
180
- patterns: |
181
- // Service with dependencies
182
- type UserService struct {
183
- repo UserRepository
184
- cache Cache
185
- logger *slog.Logger
186
- }
187
-
188
- func NewUserService(
189
- repo UserRepository,
190
- cache Cache,
191
- logger *slog.Logger,
192
- ) *UserService {
193
- return &UserService{
194
- repo: repo,
195
- cache: cache,
196
- logger: logger,
197
- }
198
- }
199
-
200
- // Wire up in main
201
- func main() {
202
- logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
203
- db := database.New(cfg.DatabaseURL)
204
- cache := redis.New(cfg.RedisURL)
205
-
206
- repo := repository.NewUserRepository(db)
207
- service := service.NewUserService(repo, cache, logger)
208
- handler := handler.NewUserHandler(service)
209
-
210
- router := handler.NewRouter(handler)
211
- http.ListenAndServe(":8080", router)
212
- }
83
+ pattern: "Struct with dependencies as fields, New* constructor functions, wire up in main()"
213
84
  ```
214
85
 
86
+ Reference: guides/go-backend/uber-style.md
87
+
215
88
  ### 6. Configuration
216
89
 
217
90
  ```yaml
@@ -219,30 +92,11 @@ approach:
219
92
  - Use environment variables
220
93
  - Validate at startup
221
94
  - Group related settings
222
-
223
- patterns: |
224
- type Config struct {
225
- Server ServerConfig
226
- Database DatabaseConfig
227
- Redis RedisConfig
228
- }
229
-
230
- type ServerConfig struct {
231
- Host string `env:"SERVER_HOST" envDefault:"0.0.0.0"`
232
- Port int `env:"SERVER_PORT" envDefault:"8080"`
233
- ReadTimeout time.Duration `env:"SERVER_READ_TIMEOUT" envDefault:"5s"`
234
- WriteTimeout time.Duration `env:"SERVER_WRITE_TIMEOUT" envDefault:"10s"`
235
- }
236
-
237
- func LoadConfig() (*Config, error) {
238
- var cfg Config
239
- if err := env.Parse(&cfg); err != nil {
240
- return nil, fmt.Errorf("parse config: %w", err)
241
- }
242
- return &cfg, nil
243
- }
95
+ pattern: "Config struct with nested typed configs, env struct tags, Parse at startup"
244
96
  ```
245
97
 
98
+ Reference: guides/go-backend/uber-style.md
99
+
246
100
  ### 7. Testing
247
101
 
248
102
  ```yaml
@@ -250,55 +104,11 @@ patterns:
250
104
  table_driven: for comprehensive coverage
251
105
  interfaces: for mocking
252
106
  parallel: for speed
253
-
254
- example: |
255
- func TestUserService_GetUser(t *testing.T) {
256
- tests := []struct {
257
- name string
258
- userID string
259
- mock func(*MockRepository)
260
- want *User
261
- wantErr error
262
- }{
263
- {
264
- name: "success",
265
- userID: "123",
266
- mock: func(m *MockRepository) {
267
- m.EXPECT().FindByID("123").Return(&User{ID: "123"}, nil)
268
- },
269
- want: &User{ID: "123"},
270
- },
271
- {
272
- name: "not found",
273
- userID: "999",
274
- mock: func(m *MockRepository) {
275
- m.EXPECT().FindByID("999").Return(nil, ErrNotFound)
276
- },
277
- wantErr: ErrNotFound,
278
- },
279
- }
280
-
281
- for _, tt := range tests {
282
- t.Run(tt.name, func(t *testing.T) {
283
- t.Parallel()
284
- ctrl := gomock.NewController(t)
285
- repo := NewMockRepository(ctrl)
286
- tt.mock(repo)
287
-
288
- svc := NewUserService(repo, nil, slog.Default())
289
- got, err := svc.GetUser(context.Background(), tt.userID)
290
-
291
- if !errors.Is(err, tt.wantErr) {
292
- t.Errorf("got error %v, want %v", err, tt.wantErr)
293
- }
294
- if diff := cmp.Diff(tt.want, got); diff != "" {
295
- t.Errorf("mismatch (-want +got):\n%s", diff)
296
- }
297
- })
298
- }
299
- }
107
+ tools: "gomock for mocks, cmp.Diff for assertions"
300
108
  ```
301
109
 
110
+ Reference: guides/go-backend/uber-style.md
111
+
302
112
  ### 8. Performance (Uber Style)
303
113
 
304
114
  ```yaml
@@ -307,24 +117,10 @@ guidelines:
307
117
  - Pre-allocate slices with known capacity
308
118
  - Avoid repeated string-to-byte conversions
309
119
  - Copy slices/maps at boundaries
310
-
311
- patterns: |
312
- // Pre-allocate
313
- items := make([]Item, 0, len(input))
314
-
315
- // strconv for conversions
316
- s := strconv.Itoa(n) // not fmt.Sprintf("%d", n)
317
-
318
- // Copy at boundaries
319
- func (s *Store) GetItems() []Item {
320
- s.mu.RLock()
321
- defer s.mu.RUnlock()
322
- items := make([]Item, len(s.items))
323
- copy(items, s.items)
324
- return items
325
- }
326
120
  ```
327
121
 
122
+ Reference: guides/go-backend/uber-style.md
123
+
328
124
  ## Application
329
125
 
330
126
  When writing Go backend code: