
代码质量度量复杂度与坏味道的自动识别实践一、质量靠人盯的不可持续性Code Review 靠资深工程师凭感觉挑毛病。这个函数太长了这圈太复杂了。感觉准但不可规模化也不可复现。人一累标准就松。今天觉得 80 行还行明天 120 行也放过。质量水位随评审者状态起伏。度量能把感觉变成数字。圈复杂度、认知复杂度、重复率都是客观指标。本文探讨如何用工具自动识别复杂度与坏味道。二、度量的计算机制圈复杂度衡量分支路径数。if、for、while、catch 每多一个复杂度加一。数值越高路径越多越难测全覆盖。坏味道是结构上的警示信号。过长函数、过大类、重复代码块都是典型。它们不直接报错却悄悄推高维护成本。下面是度量驱动的识别流程flowchart TD A[源码] -- B[解析为 AST] B -- C[计算圈复杂度] C -- D{超阈值?} D --|是| E[标记高风险函数] D --|否| F[通过] B -- G[检测重复块] G -- H{重复超阈值?} H --|是| I[标记坏味道] H --|否| F style E fill:#ffebee style I fill:#fff3e0关键在阈值的设定。太严告警轰炸太松形同虚设。应随团队成熟度分级给过渡期。三、生产级度量实现下面用ast计算函数的圈复杂度。import ast from dataclasses import dataclass from pathlib import Path dataclass class FuncMetric: name: str complexity: int lines: int class ComplexityVisitor(ast.NodeVisitor): 遍历函数按决策点累加圈复杂度 def __init__(self): self.metrics: list[FuncMetric] [] def visit_FunctionDef(self, node: ast.FunctionDef) - None: complexity 1 # 基础路径 for child in ast.walk(node): if isinstance(child, (ast.If, ast.For, ast.While, ast.AsyncFor, ast.ExceptHandler)): complexity 1 self.metrics.append(FuncMetric( namenode.name, complexitycomplexity, lines(node.end_lineno or node.lineno) - node.lineno 1, )) self.generic_visit(node) def analyze(path: Path, threshold: int 10) - list[FuncMetric]: tree ast.parse(path.read_text(encodingutf-8)) v ComplexityVisitor() v.visit(tree) return [m for m in v.metrics if m.complexity threshold] if __name__ __main__: risky analyze(Path(service.py), threshold10) for m in risky: print(f{m.name} 复杂度 {m.complexity}, 需拆分)真实系统会接重复检测如dup算法。对相似代码块做哈希比对超行数则报警。并生成趋势图看复杂度是升是降。四、边界分析与架构权衡度量有用但别被数字绑架。阈值是经验值不是真理。不同语言、不同域合理阈值不同。基础设施代码天然分支多应用代码应更简。一刀切会误伤合理设计。复杂度低不等于质量高。数字好看逻辑可能仍烂。度量是筛子不是裁判。高风险项要人审而非自动否决。坏味道的误报。抽象相似未必该合并。过度去重会引入不必要耦合。是否合并要看演变方向而非当下相似。度量疲劳。告警太多团队会无视。只把最关键指标接入门禁其余做趋势观察。门禁要少而硬观察要宽而软。质量度量的趋势比单点更有价值。一次扫描的分数意义有限长期趋势才说明问题复杂度是在升还是降坏味道是在增还是减建议把每次度量结果入库画趋势线在评审会上用趋势说话而非纠结单次数字。另一个实践是针对热点文件重点盯防变更频繁且复杂度高的文件是事故高发区应优先补测试、做重构。最后度量要嵌入流程而非运动式开展接入预提交与 CI 门禁让质量数据随每次提交自然积累而不是偶尔人工跑一次。五、总结代码质量度量本质是把主观感觉客观化。机制上用圈复杂度与重复检测量化风险。工程上用分级阈值与门禁守住底线。落地路线先选核心指标与合理阈值接 AST 自动计算高风险项人工复核门禁只卡最关键项。数字不取代判断但让判断有依据。