go-anti-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Anti-Patterns Skill
Go语言反模式处理技能
Operator Context
技能运行机制
This skill operates as an operator for Go anti-pattern detection and remediation, configuring Claude's behavior to identify over-engineering, premature abstraction, and idiomatic violations in Go code. It implements the Pattern Recognition architectural approach -- scan, detect, explain, remediate -- with Domain Intelligence embedded in Go-specific heuristics.
本技能作为Go语言反模式检测与修复的操作组件,用于配置Claude的行为,以识别Go代码中的过度工程、过早抽象以及不符合语言惯用法的问题。它采用模式识别架构方法——扫描、检测、解释、修复——并嵌入了针对Go语言的领域智能启发式规则。
Hardcoded Behaviors (Always Apply)
硬性规则(始终适用)
- CLAUDE.md Compliance: Read and follow repository CLAUDE.md before reviewing
- Over-Engineering Prevention: Flag complexity only when simpler Go exists; never add complexity while removing it
- Evidence-Based Detection: Every flagged anti-pattern must cite specific code location and explain concrete harm
- YAGNI Enforcement: Do not suggest abstractions (interfaces, generics, channels) without 2+ concrete use cases
- Preserve Working Code: Flag patterns for awareness; do not rewrite working code without explicit request
- Idiomatic Go Priority: Recommendations must align with Go proverbs and standard library conventions
- 遵循CLAUDE.md规范:在审查代码前阅读并遵循仓库中的CLAUDE.md文档
- 防止过度工程:仅当存在更简洁的Go实现方案时才标记复杂度问题;修复过程中绝不能引入新的复杂度
- 基于证据的检测:每个标记的反模式必须引用具体代码位置,并说明其造成的实际危害
- 执行YAGNI原则:在没有2个及以上具体使用场景的情况下,不建议引入抽象(接口、泛型、通道)
- 保留可运行代码:仅标记模式以引起注意;未经明确请求,不得重写可运行代码
- 优先遵循Go语言惯用法:建议必须符合Go语言谚语及标准库约定
Default Behaviors (ON unless disabled)
默认行为(默认开启,可关闭)
- Quick Detection Table: Use the detection guide to scan code systematically
- One Pattern at a Time: Address anti-patterns individually, not in bulk rewrites
- Context-Aware Severity: Rate impact as low/medium/high based on codebase context
- Show Both Versions: Present current code alongside recommended alternative
- Root Cause Explanation: Explain WHY the pattern is harmful, not just that it is
- Scope Limitation: Only flag patterns within the files under review
- 快速检测表:使用检测指南系统性地扫描代码
- 逐个处理模式:单独处理每个反模式,不进行批量重写
- 上下文感知的严重程度:根据代码库上下文将影响程度分为低/中/高
- 展示对比版本:同时呈现当前代码与推荐的替代方案
- 根因解释:解释该模式为何有害,而非仅指出其是反模式
- 范围限制:仅标记待审查文件中的模式
Optional Behaviors (OFF unless enabled)
可选行为(默认关闭,可开启)
- Full Codebase Scan: Scan entire repository for anti-pattern instances
- Metrics Collection: Count anti-pattern occurrences by type
- Auto-Refactor: Apply fixes directly instead of only flagging them
- Historical Analysis: Check git history for when anti-patterns were introduced
- 全代码库扫描:扫描整个仓库以查找反模式实例
- 指标收集:按类型统计反模式出现次数
- 自动重构:直接应用修复,而非仅标记问题
- 历史分析:检查git历史以确定反模式的引入时间
What This Skill CAN Do
本技能可实现的功能
- Detect the 7 core Go anti-patterns with code-level evidence
- Provide idiomatic Go alternatives with before/after examples
- Explain the concrete harm each pattern causes (complexity, bugs, maintenance)
- Distinguish between genuine anti-patterns and acceptable trade-offs
- Guide incremental cleanup without destabilizing working code
- 结合代码级证据检测7种核心Go语言反模式
- 提供符合Go语言惯用法的替代方案,并附带前后对比示例
- 解释每种模式造成的实际危害(复杂度、bug、维护成本)
- 区分真正的反模式与可接受的权衡方案
- 指导增量式清理,避免破坏可运行代码
What This Skill CANNOT Do
本技能无法实现的功能
- Rewrite entire codebases (use systematic-refactoring instead)
- Detect non-Go anti-patterns (use language-specific skills)
- Optimize performance (use performance profiling tools)
- Replace code review (use go-code-review for comprehensive review)
- Judge patterns without seeing the surrounding context
- 重写整个代码库(请使用systematic-refactoring技能)
- 检测非Go语言的反模式(请使用对应语言的专属技能)
- 优化性能(请使用性能分析工具)
- 替代代码审查(请使用go-code-review技能进行全面审查)
- 在不了解周边上下文的情况下评判模式
Instructions
操作步骤
Step 1: Scan for Anti-Patterns
步骤1:扫描反模式
Use the Quick Detection Guide to systematically check code under review.
| Code Smell | Detection Question | If Yes |
|---|---|---|
| Interface with one impl | Do you have 2+ implementations? | Remove interface |
| Goroutine + WaitGroup | Is work I/O bound or CPU heavy? | Use sequential loop |
| Does wrap add context? | Add operation + ID |
| Channel for return value | Is there actual concurrency? | Use regular return |
| Generic with one type | Used with multiple types? | Use concrete type |
| Context in pure function | Does function do I/O? | Remove context param |
| Tiny extracted function | Called from 2+ places? | Inline it |
使用快速检测指南系统性地检查待审查代码。
| 代码异味 | 检测问题 | 如果是 |
|---|---|---|
| 仅一个实现的接口 | 是否有2个及以上的实现? | 移除接口 |
| Goroutine + WaitGroup | 操作是I/O密集型还是CPU密集型? | 使用顺序循环 |
| 包装是否添加了上下文信息? | 添加操作内容及ID |
| 用于返回值的通道 | 是否存在实际并发场景? | 使用常规返回值 |
| 仅一种类型的泛型 | 是否用于多种类型? | 使用具体类型 |
| 纯函数中的Context | 函数是否执行I/O操作? | 移除Context参数 |
| 提取的小型函数 | 是否在2个及以上位置被调用? | 内联该函数 |
Step 2: Classify and Report
步骤2:分类并报告
For each detected anti-pattern, produce a structured report:
ANTI-PATTERN DETECTED:
- Pattern: [Name from catalog below]
- Location: [File:line]
- Issue: [What is wrong with current approach]
- Impact: [Complexity/performance/maintainability cost]
- Severity: [Low/Medium/High]
- Recommendation: [Simpler Go alternative]针对每个检测到的反模式,生成结构化报告:
检测到反模式:
- 模式:[以下目录中的名称]
- 位置:[文件:行号]
- 问题:当前实现方式的问题所在
- 影响:[复杂度/性能/可维护性成本]
- 严重程度:[低/中/高]
- 建议:[更简洁的Go语言替代方案]Step 3: Provide Remediation
步骤3:提供修复方案
Show before/after code. Reference the detailed examples in for full patterns.
references/code-examples.md展示前后对比代码。完整模式请参考中的详细示例。
references/code-examples.mdAnti-Pattern Catalog
反模式目录
AP-1: Premature Interface Abstraction
AP-1:过早接口抽象
Detection: Interface exists with exactly one implementation.
Why it harms Go code:
- Interfaces should be discovered, not invented upfront
- Go philosophy: "Accept interfaces, return concrete types"
- Adds cognitive overhead without providing flexibility
Fix: Start with concrete types. Add interface ONLY when you need 2+ implementations (e.g., adding a mock for tests or a cache layer).
go
// BAD: Interface before need
type UserRepository interface {
GetUser(id string) (*User, error)
}
type PostgresUserRepository struct{ db *sql.DB }
// Only one implementation exists
// GOOD: Concrete type first
type UserRepository struct{ db *sql.DB }
func (r *UserRepository) GetUser(id string) (*User, error) { ... }
// Add interface when second implementation appears检测方式:接口仅存在一个实现。
为何会损害Go代码:
- 接口应是被发现的,而非预先设计的
- Go语言哲学:“接受接口,返回具体类型”
- 增加认知负担却未提供灵活性
修复方案:从具体类型开始。仅当需要2个及以上实现时(例如为测试添加mock或缓存层)再添加接口。
go
// 不良示例:提前定义接口
type UserRepository interface {
GetUser(id string) (*User, error)
}
type PostgresUserRepository struct{ db *sql.DB }
// 仅存在一个实现
// 良好示例:先使用具体类型
type UserRepository struct{ db *sql.DB }
func (r *UserRepository) GetUser(id string) (*User, error) { ... }
// 当出现第二个实现时再添加接口AP-2: Goroutine Overkill for Sequential Work
AP-2:针对顺序操作滥用Goroutine
Detection: Goroutines + WaitGroup + channels for operations that are not I/O bound or CPU intensive.
Why it harms Go code:
- Concurrency overhead may exceed processing time
- Makes debugging harder (race conditions, unpredictable ordering)
- WaitGroup + channel + error handling adds significant complexity
Fix: Use a sequential loop. Add concurrency ONLY after profiling proves it helps for I/O-bound or CPU-intensive work.
go
// BAD: Goroutines for simple iteration
errCh := make(chan error, len(items))
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(item Item) { defer wg.Done(); ... }(item)
}
// GOOD: Sequential is clearer
for _, item := range items {
if err := process(item); err != nil {
return fmt.Errorf("process item %s: %w", item.ID, err)
}
}检测方式:针对非I/O密集型或非CPU密集型操作使用Goroutines + WaitGroup + 通道。
为何会损害Go代码:
- 并发开销可能超过处理时间
- 增加调试难度(竞态条件、不可预测的执行顺序)
- WaitGroup + 通道 + 错误处理会显著增加复杂度
修复方案:使用顺序循环。仅当性能分析证明对I/O密集型或CPU密集型操作有帮助时,再添加并发。
go
// 不良示例:为简单迭代使用Goroutine
errCh := make(chan error, len(items))
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(item Item) { defer wg.Done(); ... }(item)
}
// 良好示例:顺序执行更清晰
for _, item := range items {
if err := process(item); err != nil {
return fmt.Errorf("process item %s: %w", item.ID, err)
}
}AP-3: Error Wrapping Without Context
AP-3:无上下文的错误包装
Detection: Error wraps that add "error", "failed", or no meaningful information.
Why it harms Go code:
- Error messages should form a narrative from top to bottom
- Missing context makes debugging require source code inspection
- Vague wraps like "error: %w" or "failed: %w" waste the wrap
Fix: Include the operation being performed and relevant identifiers.
go
// BAD: No context
return nil, fmt.Errorf("error: %w", err)
return nil, fmt.Errorf("failed: %w", err)
// GOOD: Narrative context
return nil, fmt.Errorf("load config from %s: %w", path, err)
return nil, fmt.Errorf("parse config JSON from %s: %w", path, err)检测方式:错误包装仅添加了“error”、“failed”或无意义信息。
为何会损害Go代码:
- 错误消息应自上而下形成完整的问题描述
- 缺少上下文会导致调试时需要查看源代码
- 像“error: %w”或“failed: %w”这样模糊的包装是无效的
修复方案:包含正在执行的操作及相关标识符。
go
// 不良示例:无上下文信息
return nil, fmt.Errorf("error: %w", err)
return nil, fmt.Errorf("failed: %w", err)
// 良好示例:带有上下文的描述
return nil, fmt.Errorf("load config from %s: %w", path, err)
return nil, fmt.Errorf("parse config JSON from %s: %w", path, err)AP-4: Channel Misuse for Simple Communication
AP-4:针对简单通信误用通道
Detection: Channels used where a return value or direct function call suffices.
Why it harms Go code:
- Channels are for communication between goroutines
- Without actual concurrency, channels add ceremony without benefit
- Error handling becomes awkward (need separate error channel)
Fix: Use standard return values. Reserve channels for worker pools, fan-out/fan-in, and event streams.
go
// BAD: Channel for simple return
func GetUserName(id string) <-chan string {
ch := make(chan string, 1)
go func() { ch <- fetchUser(id).Name }()
return ch
}
// GOOD: Direct return
func GetUserName(id string) (string, error) {
user, err := fetchUser(id)
if err != nil { return "", err }
return user.Name, nil
}检测方式:在可使用返回值或直接函数调用的场景下使用通道。
为何会损害Go代码:
- 通道用于goroutine之间的通信
- 在无实际并发的场景下,通道只会增加形式化开销而无益处
- 错误处理变得繁琐(需要单独的错误通道)
修复方案:使用标准返回值。仅在工作池、扇出/扇入、事件流场景下使用通道。
go
// 不良示例:使用通道返回简单结果
func GetUserName(id string) <-chan string {
ch := make(chan string, 1)
go func() { ch <- fetchUser(id).Name }()
return ch
}
// 良好示例:直接返回结果
func GetUserName(id string) (string, error) {
user, err := fetchUser(id)
if err != nil { return "", err }
return user.Name, nil
}AP-5: Generic Abuse for Single-Use Cases
AP-5:针对单一使用场景滥用泛型
Detection: Type parameters used with only one concrete type instantiation.
Why it harms Go code:
- Generics add complexity (type parameters, constraints)
- Only valuable when you actually need multiple concrete types
- Go prioritizes simplicity over premature generalization
Fix: Use concrete types. Add generics when you have 2+ type instantiations for data structures, algorithms, or shared behavior.
go
// BAD: Generic with one type
type Container[T any] struct{ value T }
// Only ever used as Container[string]
// GOOD: Concrete type
type StringContainer struct{ value string }检测方式:类型参数仅用于一种具体类型实例化。
为何会损害Go代码:
- 泛型会增加复杂度(类型参数、约束)
- 仅当确实需要多种具体类型时才有价值
- Go语言优先考虑简洁性而非过早泛化
修复方案:使用具体类型。仅当数据结构、算法或共享行为需要2个及以上类型实例化时,再添加泛型。
go
// 不良示例:仅一种类型的泛型
type Container[T any] struct{ value T }
// 仅被用作Container[string]
// 良好示例:使用具体类型
type StringContainer struct{ value string }AP-6: Context Soup
AP-6:Context混乱
Detection: parameter in functions that perform no I/O, cancellation, or deadline checks.
context.ContextWhy it harms Go code:
- Context is for cancellation, deadlines, and request-scoped values
- In pure computation, context adds noise to signatures
- Suggests the function might do I/O when it does not
Fix: Reserve context for functions that do I/O, should be cancellable, or carry request-scoped values.
go
// BAD: Context in pure function
func CalculateTotal(ctx context.Context, prices []float64) float64 { ... }
// GOOD: No context needed
func CalculateTotal(prices []float64) float64 { ... }检测方式:参数被用于不执行I/O、取消操作或截止时间检查的函数中。
context.Context为何会损害Go代码:
- Context用于取消操作、截止时间及请求作用域的值传递
- 在纯计算函数中,Context会增加签名的冗余
- 暗示函数可能执行I/O操作,但实际并未执行
修复方案:仅在执行I/O、可取消或携带请求作用域值的函数中使用Context。
go
// 不良示例:纯函数中的Context
func CalculateTotal(ctx context.Context, prices []float64) float64 { ... }
// 良好示例:无需Context
func CalculateTotal(prices []float64) float64 { ... }AP-7: Unnecessary Function Extraction
AP-7:不必要的函数提取
Detection: Tiny functions (1-5 lines) called from exactly one place, extracted to satisfy complexity metrics.
Why it harms Go code:
- Adds indirection without adding clarity
- Reader must jump between functions to understand a simple flow
- Satisfying cyclomatic complexity tools is not a valid reason
When TO extract: Reused in 2+ places, complex enough to warrant its own name, needs independent testing, or represents a distinct logical operation.
When NOT to extract: To satisfy metrics tools, for trivial operations called once, or when the function name just describes what the code obviously does.
go
// BAD: Extracted for metrics
func (opts AuditorOpts) parsePort() (int, error) {
if opts.Port == "" { return 5672, nil }
return strconv.Atoi(opts.Port)
}
// Called exactly once from buildConnectionURL
// GOOD: Inline when called once
func (opts AuditorOpts) buildConnectionURL() (string, error) {
port := 5672
if opts.Port != "" {
var err error
port, err = strconv.Atoi(opts.Port)
if err != nil { return "", fmt.Errorf("parse port %q: %w", opts.Port, err) }
}
return fmt.Sprintf("amqp://%s:%d", opts.Host, port), nil
}检测方式:小型函数(1-5行)仅在一个位置被调用,提取该函数仅是为了满足复杂度指标。
为何会损害Go代码:
- 增加了间接性,却未提升清晰度
- 读者需要在函数间跳转才能理解简单流程
- 满足圈复杂度工具的要求并非合理理由
何时应提取函数:在2个及以上位置被复用、复杂度足够高需要独立命名、需要独立测试,或代表独立逻辑操作时。
何时不应提取函数:为满足指标工具要求、仅被调用一次的 trivial 操作,或函数名称仅描述代码显而易见的功能时。
go
// 不良示例:为满足指标提取函数
func (opts AuditorOpts) parsePort() (int, error) {
if opts.Port == "" { return 5672, nil }
return strconv.Atoi(opts.Port)
}
// 仅在buildConnectionURL中被调用一次
// 良好示例:仅被调用一次时内联
func (opts AuditorOpts) buildConnectionURL() (string, error) {
port := 5672
if opts.Port != "" {
var err error
port, err = strconv.Atoi(opts.Port)
if err != nil { return "", fmt.Errorf("parse port %q: %w", opts.Port, err) }
}
return fmt.Sprintf("amqp://%s:%d", opts.Host, port), nil
}Go-Specific Phantom Problem Indicators
Go语言特有的“伪问题”迹象
Watch for solutions looking for problems:
- Adding interfaces when concrete types suffice
- Implementing channels when simple function calls work
- Creating goroutines for inherently sequential operations
- Over-abstracting with generics for single-use cases
- Adding middleware layers for simple HTTP handlers
- Creating worker pools for low-throughput scenarios
注意那些为了解决不存在的问题而引入的方案:
- 在具体类型足够用时添加接口
- 在简单函数调用可行时实现通道
- 为本质上是顺序的操作创建goroutine
- 针对单一使用场景过度抽象泛型
- 为简单HTTP处理程序添加中间件层
- 为低吞吐量场景创建工作池
Error Handling
错误处理
Error: "False Positive -- Pattern Is Intentional"
错误:“误报——模式是有意为之”
Cause: Detected anti-pattern is actually justified by context (e.g., interface for testing boundary, generic for library API).
Solution: Check for 2+ implementations or concrete future need. If justified, note as acceptable trade-off and move on.
原因:检测到的反模式实际上符合上下文需求(例如,为测试边界添加的接口、为库API设计的泛型)。
解决方案:检查是否存在2个及以上实现或明确的未来需求。如果合理,将其标记为可接受的权衡方案并继续处理其他问题。
Error: "Refactoring Breaks Tests"
错误:“重构破坏测试”
Cause: Tests depended on the anti-pattern structure (e.g., mocking an interface that gets removed).
Solution: Update tests to use the concrete type. If removal would require major test rewrite, flag as tech debt rather than fixing immediately.
原因:测试依赖于反模式的结构(例如,模拟了一个将被移除的接口)。
解决方案:更新测试以使用具体类型。如果移除操作需要大量重写测试,将其标记为技术债务而非立即修复。
Error: "Team Disagrees on Pattern"
错误:“团队对模式存在分歧”
Cause: Different Go style preferences or legacy conventions in the codebase.
Solution: Defer to existing codebase conventions. Flag in review comments rather than changing unilaterally. Cite Go proverbs or standard library examples as evidence.
原因:代码库中存在不同的Go语言风格偏好或遗留约定。
解决方案:遵循现有代码库的约定。在审查评论中标记问题,而非单方面修改。引用Go语言谚语或标准库示例作为依据。
Examples
示例
Example 1: Code Review Detection
示例1:代码审查检测
User says: "Review this Go service for anti-patterns"
Actions:
- Scan using Quick Detection Guide table
- Flag each detected pattern with location and severity
- Provide before/after for highest-severity items
- Document findings in structured report format Result: Prioritized list of anti-patterns with remediation guidance
用户说:“审查这个Go服务中的反模式”
操作:
- 使用快速检测表进行扫描
- 标记每个检测到的模式,并注明位置和严重程度
- 为最高严重程度的问题提供前后对比方案
- 以结构化报告格式记录发现 结果:带有修复指导的优先反模式列表
Example 2: Specific Pattern Question
示例2:特定模式问题
User says: "Should I add an interface for this repository?"
Actions:
- Check how many implementations exist or are planned
- If only one: recommend concrete type (AP-1)
- If testing boundary: suggest consumer-side interface
- Explain Go's "accept interfaces, return structs" philosophy Result: Evidence-based recommendation with Go idiom context
用户说:“我应该为这个仓库添加接口吗?”
操作:
- 检查现有或计划中的实现数量
- 如果只有一个实现:建议使用具体类型(AP-1)
- 如果是测试边界:建议在消费者端定义接口
- 解释Go语言“接受接口,返回结构体”的哲学 结果:基于证据的建议,并附带Go语言惯用法上下文
References
参考资料
This skill uses these shared patterns:
- Anti-Rationalization - Prevents shortcut rationalizations
- Verification Checklist - Pre-completion checks
本技能使用以下共享模式:
- 反合理化 - 防止不合理的自我辩解
- 验证清单 - 完成前检查
Domain-Specific Anti-Rationalization
Go语言特有的反合理化
| Rationalization | Why It Is Wrong | Required Action |
|---|---|---|
| "This interface might be needed later" | YAGNI; future is unknown | Start concrete, extract when needed |
| "Goroutines make it faster" | Concurrency has overhead; profile first | Prove bottleneck exists before adding goroutines |
| "Context should be everywhere" | Context is for I/O and cancellation only | Remove from pure functions |
| "Generics make it more flexible" | Flexibility without use cases is complexity | Use concrete types until 2+ instantiations |
| "Small functions are always better" | Indirection has cognitive cost | Inline single-use trivial functions |
| 自我辩解 | 错误原因 | 要求操作 |
|---|---|---|
| “这个接口以后可能会用到” | 违反YAGNI原则;未来不可知 | 从具体类型开始,需要时再提取接口 |
| “Goroutine会让代码更快” | 并发存在开销;需先进行性能分析 | 在添加goroutine前证明存在性能瓶颈 |
| “Context应在所有地方使用” | Context仅用于I/O和取消操作 | 从纯函数中移除Context |
| “泛型会让代码更灵活” | 无使用场景的灵活度只会增加复杂度 | 在有2个及以上实例化场景前使用具体类型 |
| “小型函数总是更好” | 间接性会增加认知成本 | 内联仅被调用一次的trivial函数 |
Reference Files
参考文件
- : Extended before/after examples for all 7 anti-patterns
${CLAUDE_SKILL_DIR}/references/code-examples.md
- :所有7种反模式的扩展前后对比示例
${CLAUDE_SKILL_DIR}/references/code-examples.md