Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:02:42 +08:00
commit 34a2423d78
33 changed files with 12105 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
---
name: pre-commit-review
description: ADVISORY validation of code against design principles that linters cannot enforce. Use after linter passes and tests pass to validate design quality. Categorizes findings as Design Debt, Readability Debt, or Polish Opportunities. Does NOT block commits.
---
# Pre-Commit Design Review
Expert design analysis that detects issues linters can't catch. Returns detailed report to caller with categorized findings and fix recommendations.
## What This Skill Does
**Pure Analysis & Reporting** - Generates report, doesn't fix anything or invoke skills.
### Input
- Files to review (specific files or all staged changes)
- Review mode: `full` (first run) or `incremental` (subsequent runs)
- Previous findings (optional, for incremental mode)
- Context (invoked by refactoring, orchestrator, subagent, or user)
### Output
- Structured report with categorized findings
- Each finding: `file:line`, issue, why it matters, fix strategy, effort estimate
- Prioritized by impact and effort
- Format: Parseable for combined analysis (when invoked by orchestrator)
### Invocation Modes
**1. Direct Skill Invocation** (User or Orchestrator)
- Full control, can invoke other skills
- Can make changes based on findings
- Interactive mode with user feedback
**2. Subagent Mode** (Task tool with go-code-reviewer)
- Read-only analysis, returns report only
- Cannot invoke other skills
- Used for parallel execution by orchestrator
- Designed for speed and focused analysis
### What Reviewer Detects (That Linters Can't)
- Primitive obsession (with juiciness scoring)
- Unstorified functions (mixed abstraction levels)
- Missing domain concepts (implicit types that should be explicit)
- Non-self-validating types (defensive code in methods)
- Poor comment quality (explaining what instead of why)
- File structure issues (too long, too many types)
- Generic package extraction opportunities
- Design bugs (nil deref, ignored errors, resource leaks)
- Test quality (weak assertions, missing use cases, mock overuse, conditionals in tests)
**See [reference.md](./reference.md) for complete detection checklist with examples**
## Who Invokes This Skill
1. **@refactoring skill** - After applying patterns, validates design quality remains high
2. **@linter-driven-development** - Phase 4, checks design quality after linter passes
3. **User** - Manual code review before commit
## Workflow
### Full Review Mode (First Run)
```
1. Read all files under review (using Read tool)
2. Apply design principles checklist from reference.md (LLM reasoning)
3. Search for usage patterns across codebase (using Grep tool)
4. Categorize findings:
🐛 Bugs (nil deref, ignored errors, resource leaks)
🔴 Design Debt (types, architecture, validation)
🟡 Readability Debt (abstraction, flow, clarity)
🟢 Polish (naming, docs, minor improvements)
5. Generate structured report with recommendations
6. Return report to caller (doesn't invoke other skills or make fixes)
```
### Incremental Review Mode (Subsequent Runs)
Used after fixes have been applied to verify resolution and detect new issues.
```
1. Read ONLY changed files since last review (using git diff)
2. Compare against previous findings:
- Mark resolved issues as ✅ Fixed
- Identify issues that still exist
3. Analyze changed code for NEW issues introduced by fixes
4. Generate delta report:
- ✅ Fixed: Issues from previous run that are now resolved
- ⚠️ Remaining: Issues that still need attention
- 🆕 New: Issues introduced by recent changes
5. Return concise delta report (not full analysis)
```
**When to Use Incremental Mode:**
- After @refactoring skill applies fixes
- During iterative fix loop in Phase 4 of autopilot workflow
- User requests re-review after making changes
**Benefits:**
- Faster execution (only analyzes changed files)
- Clear feedback on what was fixed vs what remains
- Detects regressions introduced by fixes
## Detection Approach
**LLM-Powered Analysis** (not AST parsing or metrics calculation):
The reviewer reads code like a senior developer and applies design principles:
- Reads files with Read tool
- Searches patterns with Grep tool (find usages, duplications)
- Applies checklist from reference.md using LLM reasoning
- Pattern matches against anti-patterns
- Counts occurrences and calculates juiciness scores
- Generates findings with specific locations and fix guidance
**Division of Labor:**
- **Linter handles**: Complexity metrics, line counts, formatting, syntax
- **Reviewer handles**: Design patterns, domain modeling, conceptual issues
## Report Format
### Full Report (First Run)
```
📊 CODE REVIEW REPORT
Scope: [files reviewed]
Mode: FULL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total findings: 18
🐛 Bugs: 2 (fix immediately)
🔴 Design Debt: 5 (fix before commit)
🟡 Readability Debt: 8 (improves maintainability)
🟢 Polish: 3 (nice to have)
Estimated fix effort: 3.5 hours
[Detailed findings by category]
[Recommendations by priority]
[Skills to use for fixes]
```
### Incremental Report (Subsequent Runs)
```
📊 CODE REVIEW DELTA REPORT
Scope: [changed files only]
Mode: INCREMENTAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Fixed: 4 (resolved from previous run)
⚠️ Remaining: 2 (still need attention)
🆕 New: 1 (introduced by recent changes)
[Detailed delta findings]
```
### Structured Output for Orchestrator Parsing
When invoked as subagent for combined analysis, output follows strict format:
```
🐛 BUGS
────────────────────────────────────────────────
file:line | Issue description | Why it matters | Fix strategy | Effort: [Trivial/Moderate/Significant]
🔴 DESIGN DEBT
────────────────────────────────────────────────
file:line | Issue description | Why it matters | Fix strategy | Effort: [Trivial/Moderate/Significant]
🟡 READABILITY DEBT
────────────────────────────────────────────────
file:line | Issue description | Why it matters | Fix strategy | Effort: [Trivial/Moderate/Significant]
🟢 POLISH
────────────────────────────────────────────────
file:line | Issue description | Why it matters | Fix strategy | Effort: [Trivial/Moderate/Significant]
```
**Effort Estimates:**
- **Trivial**: <5 minutes (extract constant, rename variable)
- **Moderate**: 5-20 minutes (extract function, storifying, create simple type)
- **Significant**: >20 minutes (architectural refactoring, complex type extraction)
**file:line Format:** Must be exact for orchestrator to correlate with linter errors
- Example: `pkg/parser.go:45`
- NOT: `parser.go line 45` or `pkg/parser.go (line 45)`
**See [examples.md](./examples.md) for complete report examples**
## What This Skill Does NOT Do
- ❌ Invoke other skills (@refactoring, @code-designing, @testing)
- ❌ Fix anything or make code changes
- ❌ Make decisions on behalf of user
- ❌ Parse AST or calculate complexity metrics (linter does this)
- ❌ Run linter (caller does this)
- ❌ Iterate or loop (caller decides whether to re-invoke)
- ❌ Block commits (findings are advisory)
## Integration with Other Skills
### Invoked by @refactoring
```
Refactoring completes → invoke reviewer → analyze report:
- Bugs found? → Fix immediately, re-run linter
- Design debt found? → Apply another refactoring pattern
- All clean? → Return success to orchestrator
```
### Invoked by @linter-driven-development
```
Phase 4 (after linter passes):
1. Invoke reviewer on all staged changes
2. Receive categorized report
3. Present findings to user with options:
- Commit as-is (accept debt knowingly)
- Fix critical issues only (bugs + design debt)
- Fix all recommended (bugs + design + readability)
- Fix everything (including polish)
4. Based on user choice:
- Invoke @refactoring or @code-designing for chosen fixes
- Return to Phase 3 (linter loop)
- Iterate until user satisfied
```
### Invoked by User
```
Manual review request:
1. User invokes: @pre-commit-review on path/to/file.go
2. Receive detailed report
3. User decides how to proceed
4. User may invoke @refactoring or @code-designing for fixes
```
## Review Scope
**Primary Scope**: Changed code in commit
- All modified lines
- All new files
- Specific focus on design principle adherence
**Secondary Scope**: Context around changes
- Entire files containing modifications
- Flag patterns/issues outside commit scope (in BROADER CONTEXT section)
- Suggest broader refactoring opportunities if valuable
## Advisory Nature
**This review does NOT block commits.**
Purpose:
- ✅ Provide visibility into design quality
- ✅ Offer concrete improvement suggestions with examples
- ✅ Help maintain coding principles
- ✅ Guide refactoring decisions
Caller (or user) decides:
- Commit as-is (accept debt knowingly)
- Fix critical debt before commit (bugs, major design issues)
- Fix all debt before commit (comprehensive cleanup)
- Expand scope to broader refactor (when broader context issues found)
## Finding Categories
Findings are categorized by technical debt type and severity:
### 🐛 Bugs
**Will cause runtime failures or correctness issues**
- Nil dereferences, ignored errors, resource leaks
- Invalid nil returns, race conditions
- Fix immediately before any other work
### 🔴 Design Debt
**Will cause pain when extending/modifying code**
- Primitive obsession, missing domain types
- Non-self-validating types
- Wrong architecture (horizontal vs vertical)
- Fix before commit recommended
### 🟡 Readability Debt
**Makes code harder to understand and work with**
- Mixed abstraction levels, not storified
- Functions too long or complex
- Poor naming, unclear intent
- Fix improves team productivity
### 🟢 Polish Opportunities
**Minor improvements for consistency and quality**
- Non-idiomatic naming, missing examples
- Comment improvements, minor refactoring
- Low priority, nice to have
**See [reference.md](./reference.md) for detailed principles and examples for each category**
## Key Capabilities
**Detects 8 Issue Categories:**
1. Primitive Obsession - with juiciness scoring algorithm
2. Storifying - detects mixed abstraction levels
3. Missing Domain Concepts - identifies implicit types
4. Self-Validating Types - finds defensive code patterns
5. Comment Quality - analyzes what vs why
6. File Structure - checks size and responsibility boundaries
7. Testing Approach - validates test structure and quality
8. Design Bugs - catches common runtime issues
**For complete detection patterns and examples, see [reference.md](./reference.md)**
**For real-world review scenarios, see [examples.md](./examples.md)**
## Integration with Orchestrator
This skill is automatically invoked by @linter-driven-development workflow:
- **Phase 4**: Design review after linter passes
- **Iterative**: Re-invoked after fixes until clean or user accepts debt
- **Advisory**: Never blocks, always presents options
See [linter-driven-development workflow](../linter-driven-development/SKILL.md) for complete flow.

View File

@@ -0,0 +1,832 @@
# Pre-Commit Review Examples
Real-world examples of design review findings with before/after comparisons.
## Example 0: Detection Workflow
Shows how the pre-commit-review skill analyzes code step-by-step using LLM reasoning.
### Input: user/service.go
```go
package user
type UserService struct {
Repo Repository
}
func (s *UserService) CreateUser(id string, email string) error {
// Validate ID
if id == "" {
return errors.New("empty id")
}
// Validate email
if !strings.Contains(email, "@") {
return errors.New("invalid email")
}
// Open DB
db, err := sql.Open("postgres", connStr)
if err != nil {
return err
}
defer db.Close()
// Build query
query := "INSERT INTO users (id, email) VALUES (?, ?)"
_, err = db.Exec(query, id, email)
if err != nil {
return err
}
// Send email
_ = sendEmail(email, "Welcome!")
return nil
}
```
### LLM Analysis Step-by-Step
**Step 1: Apply Primitive Obsession Check (reference.md #1)**
Questions LLM asks:
- Q: Are primitives being validated?
- A: Yes - 'id' string validated at line 9, 'email' string validated at line 14
- Q: Is same validation repeated elsewhere?
- A: [Uses Grep tool to search codebase]
- Found in handler.go:23 (same id validation)
- Found in repository.go:45 (same email validation)
- Q: Would types add value beyond validation?
- A: Yes
- Email: Complex validation pattern, domain concept
- UserID: Validation scattered, type safety benefit
- Q: Calculate juiciness score (see reference.md):
```
UserID:
- Validation in 3 places (+1)
- Simple empty check (+0)
- Used in 8 locations (+2)
Score: 3/10 → MEDIUM priority
Email:
- Validation in 3 places (+1)
- Regex validation possible (+3)
- Domain/LocalPart methods (+2)
- Used in 8 locations (+2)
Score: 8/10 → HIGH priority
```
**Finding identified:**
```
🔴 Design Debt: Primitive obsession
- string id → UserID type (MEDIUM juiciness)
- string email → Email type (HIGH juiciness)
```
---
**Step 2: Apply Storifying Check (reference.md #2)**
Questions LLM asks:
- Q: Does function read like a story?
- A: No - mixes abstraction levels:
- Lines 7-16: High-level validation
- Lines 18-23: Low-level DB connection setup
- Lines 25-31: SQL implementation details
- Lines 33-34: High-level notification
- Q: Can I explain flow in 3-5 clear steps?
- A: Should be: validateUser() → saveUser() → notifyUser()
But currently mixes infrastructure and business logic
**Finding identified:**
```
🟡 Readability Debt: Function not storified
Mixed abstraction levels in CreateUser
Should extract: validateUser(), saveUser(), notifyUser()
```
---
**Step 3: Apply Self-Validating Types Check (reference.md #3)**
Questions LLM asks:
- Q: Do methods check if struct fields are nil/invalid?
- A: No checks in this method, but...
- Q: Could UserService.Repo be nil?
- A: Yes - struct can be created without validation
```go
svc := &UserService{} // Repo is nil!
svc.CreateUser(...) // Will panic if we use Repo
```
- Q: Is there a constructor with validation?
- A: [Searches codebase] No NewUserService constructor found
**Finding identified:**
```
🔴 Design Debt: Non-self-validating type
UserService can be created with nil Repo
Need NewUserService constructor
```
---
**Step 4: Apply Design Bugs Check (reference.md #8)**
Questions LLM asks:
- Q: Potential nil dereferences?
- A: Not obvious in this function (but found UserService.Repo issue above)
- Q: Are errors ignored?
- A: YES! Line 34: `_ = sendEmail(email, "Welcome!")`
Silent failure, no visibility into delivery failures
- Q: Resources cleaned up?
- A: Yes, defer db.Close() at line 23 ✓
- Q: Context propagated?
- A: No ctx parameter, but no downstream calls needing it
**Finding identified:**
```
🐛 Bug: Ignored error
Line 34: _ = sendEmail() silently ignores failure
Should at least log the error
```
---
**Step 5: Apply Other Checks**
- Abstraction Levels (#4): Already covered in storifying
- Architecture (#5): Single file, can't assess pattern
- Naming (#6): Names are clear and idiomatic ✓
- Testing (#7): No test file in scope
- Comment Quality: Missing (no package docs, no function docs)
- File Structure: Single type, reasonable size ✓
---
### Analysis Complete - Categorize & Prioritize
**Bugs (Priority 1 - Fix immediately):**
1. Ignored error at line 34
**Design Debt (Priority 2 - Fix before commit):**
2. Primitive obsession: string email (HIGH juiciness: 8/10)
3. Primitive obsession: string id (MEDIUM juiciness: 3/10)
4. Non-self-validating: UserService
**Readability Debt (Priority 3 - Improves maintainability):**
5. Not storified: CreateUser function
---
### Output Report
```
📊 CODE REVIEW REPORT
Generated: 2025-11-09 15:45:00
Scope: user/service.go (1 file, 40 lines)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total findings: 5
🐛 Bugs: 1 (fix immediately)
🔴 Design Debt: 3 (fix before commit recommended)
🟡 Readability Debt: 1 (improves maintainability)
🟢 Polish: 0
Estimated fix effort: 50 minutes total
- Critical (bugs + high juiciness design): 25 min
- Recommended (medium design + readability): 25 min
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🐛 BUGS (1) - FIX IMMEDIATELY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Ignored error
Location: user/service.go:34
Code: _ = sendEmail(email, "Welcome!")
Issue: Email sending failure silently ignored
Impact: No visibility into delivery failures, hard to debug
Fix: Log error at minimum:
if err := sendEmail(email, "Welcome!"); err != nil {
log.Printf("failed to send welcome email: %v", err)
}
Better: Return error if critical:
if err := sendEmail(email, "Welcome!"); err != nil {
return fmt.Errorf("send welcome email: %w", err)
}
Effort: 2 min
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔴 DESIGN DEBT (3) - FIX BEFORE COMMIT RECOMMENDED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Primitive obsession: string email (HIGH juiciness: 8/10)
Locations: Line 6, 14, 28, 34
Also found in: handler.go:23, repository.go:45
Juiciness Score: 8/10
- Validation in 3 places (+1)
- Complex regex validation possible (+3)
- Methods: Domain(), LocalPart() (+2)
- Used in 8 locations (+2)
Current:
func CreateUser(id string, email string) error {
if !strings.Contains(email, "@") {
return errors.New("invalid email")
}
// ...
}
Better:
type Email string
func ParseEmail(s string) (Email, error) {
if !emailRegex.MatchString(s) {
return "", fmt.Errorf("invalid email: %s", s)
}
return Email(s), nil
}
func (e Email) Domain() string { /* ... */ }
func (e Email) LocalPart() string { /* ... */ }
func (e Email) String() string { return string(e) }
func CreateUser(id string, email Email) error {
// No validation needed, guaranteed valid
}
Why: Type safety, centralized validation, prevents invalid emails
Fix: Use @code-designing skill → Create Email type
Effort: 20 min
2. Primitive obsession: string id (MEDIUM juiciness: 3/10)
Locations: Line 6, 9, 28
Also found in: handler.go:23, repository.go:45
Juiciness Score: 3/10
- Validation in 3 places (+1)
- Simple empty check (+0)
- Used in 8 locations (+2)
Note: Borderline case. Judgment call on whether to create type.
Better:
type UserID string
func ParseUserID(s string) (UserID, error) {
if s == "" {
return "", errors.New("empty user id")
}
return UserID(s), nil
}
Why: Centralizes validation, type safety
Fix: Use @code-designing skill → Create UserID type
Effort: 10 min
3. Non-self-validating type: UserService
Location: user/service.go:4
Issue: UserService.Repo is public, can be nil
No constructor to validate dependencies
Current:
type UserService struct {
Repo Repository // Can be nil!
}
svc := &UserService{} // Invalid state allowed
Better:
type UserService struct {
repo Repository // Private
}
func NewUserService(repo Repository) (*UserService, error) {
if repo == nil {
return nil, errors.New("repo required")
}
return &UserService{repo: repo}, nil
}
func (s *UserService) CreateUser(id string, email Email) error {
// No nil checks needed - constructor guarantees validity
return s.repo.Save(...)
}
Why: Impossible to create invalid service, eliminates defensive checks
Fix: Use @code-designing skill → Add constructor
Effort: 10 min
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🟡 READABILITY DEBT (1) - IMPROVES MAINTAINABILITY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Function not storified: CreateUser
Location: user/service.go:6-36
Issue: Mixes 3 abstraction levels:
- Lines 7-16: High-level validation
- Lines 18-31: Low-level DB connection/SQL
- Lines 33-34: High-level notification
Flow not clear at a glance, hard to test pieces independently.
Better:
func CreateUser(id string, email Email) error {
if err := validateUser(id, email); err != nil {
return err
}
if err := saveUser(id, email); err != nil {
return err
}
if err := notifyUser(email); err != nil {
return err
}
return nil
}
func validateUser(id string, email Email) error { /* ... */ }
func saveUser(id string, email Email) error { /* ... */ }
func notifyUser(email Email) error { /* ... */ }
Why: Reads like a story, testable pieces, clear intent
Fix: Use @refactoring skill → Storifying pattern
Effort: 15 min
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RECOMMENDATIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Priority 1: Fix immediately (2 min)
☐ Fix ignored error (log or return)
Priority 2: Fix before commit (40 min)
☐ Create Email type (HIGH juiciness) @code-designing
☐ Create UserID type (MEDIUM juiciness) @code-designing
☐ Add NewUserService constructor @code-designing
Priority 3: Improve maintainability (15 min)
☐ Storify CreateUser function @refactoring
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SKILLS TO USE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@code-designing: For creating Email, UserID types and NewUserService
@refactoring: For storifying CreateUser function
Manual: For fixing ignored error (simple change)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
END OF REPORT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
This example demonstrates how the reviewer skill applies the complete checklist from reference.md systematically, using LLM reasoning to detect issues that linters cannot catch.
---
## Example 1: Primitive Obsession + Self-Validating Types
### Before (Design Debt 🔴)
```go
package user
type UserService struct {
DB *sql.DB // Might be nil
}
func (s *UserService) CreateUser(id string, email string) error {
// Defensive check
if s.DB == nil {
return errors.New("db is nil")
}
// Primitive validation
if id == "" {
return errors.New("id required")
}
if !strings.Contains(email, "@") {
return errors.New("invalid email")
}
// Business logic
_, err := s.DB.Exec("INSERT INTO users (id, email) VALUES ($1, $2)", id, email)
return err
}
```
**Review Findings:**
- 🔴 **Design Debt**: Primitive obsession on `id` and `email`
- 🔴 **Design Debt**: Non-self-validating type (`UserService.DB` might be nil)
### After (No Debt)
```go
package user
type UserID string
type Email string
func NewUserID(s string) (UserID, error) {
if s == "" {
return "", errors.New("id required")
}
return UserID(s), nil
}
func NewEmail(s string) (Email, error) {
if !strings.Contains(s, "@") {
return "", errors.New("invalid email")
}
return Email(s), nil
}
type UserService struct {
db *sql.DB
}
func NewUserService(db *sql.DB) (*UserService, error) {
if db == nil {
return nil, errors.New("db is required")
}
return &UserService{db: db}, nil
}
func (s *UserService) CreateUser(id UserID, email Email) error {
// No validation needed - types guarantee validity
_, err := s.db.Exec("INSERT INTO users (id, email) VALUES ($1, $2)", id, email)
return err
}
```
---
## Example 2: Mixed Abstraction Levels + Storifying
### Before (Readability Debt 🟡)
```go
func ProcessPayment(orderID string, amount float64) error {
// High-level: validation
if orderID == "" {
return errors.New("invalid order id")
}
if amount <= 0 {
return errors.New("invalid amount")
}
// Low-level: HTTP client setup
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest("POST", "https://api.payment.com/charge", nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("API_KEY"))
req.Header.Set("Content-Type", "application/json")
// Low-level: JSON marshaling
body := map[string]interface{}{
"order_id": orderID,
"amount": amount,
}
jsonBody, err := json.Marshal(body)
if err != nil {
return err
}
req.Body = io.NopCloser(bytes.NewReader(jsonBody))
// Low-level: HTTP call
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// High-level: logging
log.Printf("Payment processed for order %s", orderID)
return nil
}
```
**Review Findings:**
- 🟡 **Readability Debt**: Mixed abstraction levels (business + HTTP details)
- 🟡 **Readability Debt**: Function not storified (hard to see flow)
### After (No Debt)
```go
func ProcessPayment(orderID OrderID, amount Amount) error {
if err := validatePayment(orderID, amount); err != nil {
return err
}
if err := chargePaymentGateway(orderID, amount); err != nil {
return err
}
logPaymentSuccess(orderID)
return nil
}
func validatePayment(orderID OrderID, amount Amount) error {
// Validation logic only (already validated by types, but could have business rules)
return nil
}
func chargePaymentGateway(orderID OrderID, amount Amount) error {
// HTTP client logic encapsulated
client := newPaymentClient()
return client.Charge(orderID, amount)
}
func logPaymentSuccess(orderID OrderID) {
log.Printf("Payment processed for order %s", orderID)
}
```
---
## Example 3: Horizontal Layers → Vertical Slices
### Before (Design Debt 🔴)
```
project/
├── domain/
│ └── user.go
├── service/
│ └── user_service.go
├── repository/
│ └── user_repository.go
└── handler/
└── user_handler.go
```
**Review Finding:**
- 🔴 **Design Debt**: Horizontal layering instead of vertical slices
- Impact: User feature changes require touching 4 different directories
### After (No Debt)
```
project/
└── user/
├── user.go # Domain type
├── service.go # Business logic
├── repository.go # Persistence
├── handler.go # HTTP
└── user_test.go
```
**Benefits:**
- All user-related code in one place
- Easy to understand complete feature
- Independent testing/deployment
---
## Example 4: Generic Naming
### Before (Readability Debt 🟡)
```go
package common
type DataManager struct {
store Storage
}
func (m *DataManager) ProcessData(data interface{}) (interface{}, error) {
// ...
}
func HandleRequest(ctx context.Context, data map[string]interface{}) error {
// ...
}
```
**Review Findings:**
- 🟡 **Readability Debt**: Generic package name (`common`)
- 🟡 **Readability Debt**: Vague type name (`DataManager`)
- 🟡 **Readability Debt**: Meaningless function names (`ProcessData`, `HandleRequest`)
### After (No Debt)
```go
package user
type Service struct {
repo Repository
}
func (s *Service) Create(ctx context.Context, u User) error {
// ...
}
func (s *Service) Authenticate(ctx context.Context, credentials Credentials) (Token, error) {
// ...
}
```
---
## Example 5: Testing Anti-Patterns
### Before (Design Debt 🔴)
```go
package user // Same package
// Testing private function
func TestValidateEmailInternal(t *testing.T) {
assert.True(t, validateEmailInternal("test@example.com"))
}
// Heavy mocking
func TestCreateUser(t *testing.T) {
mockRepo := &MockRepository{}
mockEmailer := &MockEmailer{}
mockRepo.On("Save", mock.Anything).Return(nil)
mockEmailer.On("Send", mock.Anything).Return(nil)
svc := &UserService{
Repo: mockRepo,
Emailer: mockEmailer,
}
err := svc.CreateUser("123", "test@example.com")
assert.NoError(t, err)
mockRepo.AssertExpectations(t)
}
// Flaky with time.Sleep
func TestAsyncOperation(t *testing.T) {
go doAsyncWork()
time.Sleep(100 * time.Millisecond) // ❌ Flaky
assert.True(t, workCompleted)
}
```
**Review Findings:**
- 🔴 **Design Debt**: Testing private methods
- 🔴 **Design Debt**: Using mocks instead of real implementations
- 🔴 **Design Debt**: Flaky test with time.Sleep
### After (No Debt)
```go
package user_test // External package
// Test public API only
func TestService_CreateUser(t *testing.T) {
// Use real implementations
repo := user.NewInMemoryRepository()
emailer := user.NewTestEmailer()
svc, err := user.NewUserService(repo, emailer)
require.NoError(t, err)
id, _ := user.NewUserID("123")
email, _ := user.NewEmail("test@example.com")
u := user.User{ID: id, Email: email}
err = svc.CreateUser(context.Background(), u)
assert.NoError(t, err)
// Verify via public API
retrieved, err := svc.GetUser(context.Background(), id)
assert.NoError(t, err)
assert.Equal(t, email, retrieved.Email)
}
// No flakiness with channels
func TestAsyncOperation(t *testing.T) {
done := make(chan struct{})
go func() {
doAsyncWork()
close(done)
}()
select {
case <-done:
// Success
case <-time.After(1 * time.Second):
t.Fatal("timeout waiting for async work")
}
}
```
---
## Example 6: Complete Commit Review Output
### Scenario
Developer adds user authentication feature with some design issues.
### Review Output
```
📋 COMMIT READINESS SUMMARY
✅ Linter: Passed (0 issues)
✅ Tests: 87% coverage (5 new types, 23 test cases)
⚠️ Design Review: 5 findings (see below)
🎯 COMMIT SCOPE
Modified:
- user/service.go (+120, -30 lines)
- user/auth.go (new file, +85 lines)
Added:
- user/user_id.go (new type: UserID)
- user/password.go (new type: Password)
Tests:
- user/service_test.go (+95 lines)
- user/auth_test.go (new, +140 lines)
⚠️ DESIGN REVIEW FINDINGS
🔴 DESIGN DEBT (Recommended to fix):
1. user/service.go:67 - Primitive obsession on session token
Current: func CreateSession(userID UserID) (string, error)
Better: func CreateSession(userID UserID) (SessionToken, error)
Why: Session tokens should be validated types to prevent empty/invalid tokens
Fix: Use @code-designing to create SessionToken type with validation
2. user/auth.go:34 - Non-self-validating type
Current:
type Authenticator struct {
HashCost int // Could be invalid
}
Better:
func NewAuthenticator(hashCost int) (*Authenticator, error) {
if hashCost < 4 || hashCost > 31 {
return nil, errors.New("invalid hash cost")
}
// ...
}
Why: Constructor should validate, methods shouldn't need defensive checks
Fix: Use @code-designing to add validating constructor
🟡 READABILITY DEBT (Consider fixing):
3. user/auth.go:89 - Mixed abstraction levels in Authenticate()
Function mixes high-level auth flow with low-level bcrypt details
Why: Harder to understand auth logic at a glance
Fix: Use @refactoring to extract password comparison to separate function
4. user/service.go:45 - Function could be storified better
Current: validateAndCreateUser() does validation + creation in one function
Better: Split into validateUser() and createUser() for clarity
Why: Single responsibility, easier to test each part
Fix: Use @refactoring to split responsibilities
🟢 POLISH OPPORTUNITIES:
5. user/auth.go:12 - Less idiomatic naming
Current: ComparePasswordWithHash
Better: PasswordMatches
Why: More concise, Go-style naming
📝 BROADER CONTEXT:
While reviewing user/service.go, noticed email is still stored as string type
(line 23). Consider refactoring to use Email type consistently across the file
for better type safety (similar to UserID change in this commit).
💡 SUGGESTED COMMIT MESSAGE
Add user authentication with self-validating types
- Introduce UserID and Password self-validating types
- Implement Authenticator with bcrypt password hashing
- Add CreateSession and Authenticate methods
- Achieve 87% test coverage with real bcrypt testing
Follows primitive obsession principles with type-safe IDs and passwords.
────────────────────────────────────────
Would you like to:
1. Commit as-is (5 design findings remain)
2. Fix design debt only (🔴 items 1-2), then commit
3. Fix design + readability debt (🔴🟡 items 1-4), then commit
4. Fix all findings including polish (🔴🟡🟢 all items), then commit
5. Expand scope to refactor email type throughout file, then commit
```

File diff suppressed because it is too large Load Diff