ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI写代码复杂度治理Skill:给代码装上刹车防过度设计

AI写代码复杂度治理Skill:给代码装上刹车防过度设计 最近被AI写代码搞到头疼的人应该不止我一个。明明只让它加一个小功能它顺手给你重构了半个模块还额外引入了一层抽象。代码量翻倍逻辑绕了三道弯测试倒是都过了但三个月后再看这堆代码自己都想不起当初为什么要这样写。这不是模型能力的问题而是我们压根没给AI立规矩。今天要聊的就是专门治这个毛病的玩法给AI写代码配一个复杂度治理Skill。它的核心不是限制AI别写代码而是让AI在动手之前先想清楚“这段复杂度值不值”把越来越乱的代码拦在提交之前。这个东西适合谁只要是日常用AI写代码、又对代码可维护性有要求的人都值得试试。不管你是个人开发者还是团队负责人都可以按下面的思路搭一套自己的Skill让AI从“埋头写代码”变成“先算账再动手”。1. 到底发生了什么AI的“过度设计”病根在哪1.1 不是AI笨是缺少一个“劝阻机制”我见过很多人抱怨AI写代码越来越复杂。起初只是拼个接口它给你生成一个Factory加Strategy再加Observer三个设计模式层层嵌套。你让它加一个开关它给你搞出一套配置中心。到底是谁教的看看我们喂给它的提示词就明白了“请写出高质量、可扩展、可维护的代码”——这句话本身就带着问题。“高质量”“可扩展”“可维护”在AI眼里是什么样的大概率对应的是教科书里的优美示例抽象、接口、依赖注入、设计模式、泛化封装。AI的训练数据里那些被反复点赞的代码恰好长这样。于是你让它写简单点它理解成“写标准点”你让它别过度设计它判断不出来什么叫“过度”。它不是故意搞复杂是它默认复杂才是专业。说白了AI缺一个约束偏差的机制。1.2 复杂度失控的几个典型现场结合我自己和团队实际遇到的场景AI写代码越写越复杂通常有这几种表现你可以对照一下自己有没有踩过小函数放大炮一个函数只需要做个映射AI生成了泛型接口还预留了异步并发扩展位结果调用点全都跟着复杂了。模式堆砌新增需求明明是简单分支判断AI硬套了策略模式还配了一个注册表加一个枚举映射改一个逻辑要动四个文件。抽象早产在一个只运行一次的业务脚本里AI也给你抽出“可复用组件”实际上根本没有第二个使用场景。防御性地狱每个参数都判空、每个函数都补异常、每步操作都打日志代码量翻倍关键逻辑淹没在防御代码里。这些问题的共同点是AI把“可能的需求”当成了“当前的需求”把“未来的扩展点”当成了“现在的实现项”。它不是不会写简单代码是缺少一个判断“什么算够用”的标准。1.3 为什么普通提示词管不住有人可能会说“那你就在提示词里写清楚‘不允许过度设计’啊。”我试过效果非常有限。原因很简单提示词是一次性的每次会话AI都会忘掉之前的口头约定。就算同一个对话里记住了换一个会话又要重新说一遍而且AI对“过度设计”的理解还是模糊的它不知道具体到什么程度算过度。所以我后来想明白一个事把这个约束做成一个Skill把模糊的要求固化成一套可以执行的、有具体判断标准的规则集让AI每次写代码之前都先加载这套规则。这就相当于给AI装了一个“复杂度刹车”。不是靠心情提醒而是靠流程卡住。2. 先搞清楚Skill到底是个什么东西2.1 Skill不是插件也不是普通提示词很多AI编程工具比如Claude Code、Codex、Cline这些目前都在推Skill机制叫法可能不太一样有的叫Agent Skills有的叫Custom Instructions有的叫Rules。但思路是相通的把一整块可复用的能力——提示词模板、规则约束、脚本、参考案例、工作流——打包成一个结构化的文件夹AI在任务需要时能主动加载这套东西。你可以把它理解成一份“工作手册”加“质量红线”AI写代码前先翻手册知道自己应该按什么标准干活干完之后再对照红线自检不合格就打回。这和普通提示词有本质区别提示词是口头交代Skill是可沉淀的流程资产。举一个生活化的例子。你请了一个助手帮你整理文档口头说“整理干净一点”他完全不知道干净的标准是什么。但你如果给他一份《文档整理手册》里面写着标题层级怎么用、段落多长合适、什么内容必须删掉、什么格式算通过他就能稳定地交付。Skill干的就是这件事。2.2 Skill的核心组成部分一个合格的Skill通常包含这几类东西具体用什么取决于你想让AI执行什么任务主描述文件告诉AI这个Skill是干嘛的、什么时候触发、怎么使用。相当于使用说明书。硬性规则用MUST、SHOULD、NEVER这类关键词写明底线。比如单函数长度上限、嵌套层数限制、禁止无依据的泛化。工作流定义把任务拆成步骤让AI按顺序执行。比如先分析、再报告、后动手改。参考样例提供好代码和坏代码的对比AI可以照着标准自查。脚本工具有些Skill会带少量脚本用于代码度量圈复杂度、函数长度、重复代码检测让AI的“感觉”变成精确数据。用这个机制去管代码复杂度恰好对症AI不是不懂“简单”的价值而是需要一个可度量的、每次都能触发的约束流程。2.3 为什么这条路比换模型靠谱顺嘴提一句最近经常看到有人在问“哪个AI写代码厉害”。我的观点是现阶段主流大模型写单点功能都不差真正拉开差距的早就不是模型本身了而是你会不会给模型搭框架。你给AI一份“不许怎么怎么写”的规则清单和完全放任AI自由发挥产出质量的差异可能比换一个大模型还明显。我见过有人天天换模型换来换去还是被AI带偏也见过有人把一个模型用出花来因为他把约束、校验、反馈闭环全做在了模型外面。复杂度治理Skill走的就是后面这条路。3. 手把手做一个“代码复杂度治理”Skill3.1 先定调你到底想让AI管什么动手做Skill之前先不要把范围铺太宽。我第一次做的时候想把“代码风格、命名规范、架构设计、测试覆盖”全塞进去结果AI每次写代码都被一大堆规则卡住不敢动笔产出质量反而更差。后来我把范围压缩到只盯一件事防止代码复杂度失控。这个Skill不管缩进空格不管变量命名只管代码是不是在往不可维护的方向滑。要想清楚一个逻辑模型默认“写得复杂才算好”所以这个Skill的任务是给它一个相反方向的约束并且给出可执行的判断标准。我定的治理维度有四个尺寸失控单文件太长、单函数太长、参数太多。嵌套失控条件套判断、循环套逻辑层层叠叠读起来像迷宫。抽象失控没有实际复用场景却提前做了泛化、接口设计、模式封装。重复失控同一段逻辑到处粘贴复制改了A处漏了B处。这四个维度不需要AI“感觉”复杂而是可以用数字指标来判定AI执行起来不会犯迷糊。3.2 Skill的目录结构与主文件我建议把Skill做成一个自包含的文件夹固定在某个目录下方便跨项目复用。目录大致长这样skills/code-complexity-guard/ ├── SKILL.md ├── rules/ │ ├── limits.md │ ├── abstraction.md │ └── duplication.md ├── templates/ │ └── review-report.md ├── scripts/ │ └── measure_complexity.py └── references/ ├── good_sample.py └── bad_sample.py核心是SKILL.md里面写明触发条件和执行流程。AI看到关键词“写代码、加功能、重构、Review”时就应该主动想起加载这个Skill。SKILL.md的内容不用长但信息要精准。我写了一个精简版本# Code Complexity Guard ## 用途 在AI编写或修改代码时控制代码复杂度防止过度设计、 函数臃肿、嵌套过深、重复堆积。 ## 触发场景 - 用户要求新写一个函数/模块 - 用户要求修改/重构已有代码 - 用户要求Review代码 ## 执行流程 1. 读取 rules/ 下的三条规则文件 2. 对涉及代码做复杂度度量调用 scripts/measure_complexity.py 或人工估算 3. 若超过阈值先给报告templates/review-report.md再提出简化方案 4. 未经用户确认不得直接大幅重构 ## 红线 - NEVER 为了“未来可能扩展”而抽象当前不需要的东西 - MUST 在单函数超过50行时主动拆解并说明理由 - MUST 在每个抽象引入时向用户举证这个抽象现在解决了什么问题 - SHOULD 尽量保持 diff 最小不做无关重构3.3 硬规则要“可说可查”不要“凭感觉”我把规则文件写成三条每一条都配合具体阈值和解释。先看第一条limits.md# 尺寸与嵌套限制 ## 函数长度 - 单函数建议不超过30行硬上限50行 - 超过50行的函数必须拆解拆解后的每个子函数职责必须单一 - 如果超过50行且无法拆解需要在注释里写明原因 ## 参数数量 - 函数参数建议不超过3个硬上限5个 - 超过5个参数时应优先考虑合并参数对象而不是直接追加参数 ## 嵌套深度 - if/for/while 嵌套建议不超过3层 - 超过3层时需要考虑提前返回、提取判断条件为独立函数 - 禁止出现“回调地狱”式的链式缩进 ## 圈复杂度 - 单个函数的圈复杂度建议不超过10 - 如果已经超过10必须拆解分支逻辑这里有个很重要的点阈值不能拍脑袋要给AI和使用者共同可解释的依据。比如“参数超过5个”背后其实是心理学里的工作记忆容量——人同时能盯住的变量就那么多参数一多必然靠猜。这些数字本身可以讨论不同团队可以调但一定要有而不是让AI自己“看着办”。再看第二条abstraction.md# 抽象与设计约束 ## 前置检验 - 抽象前必须回答当前代码中这个抽象有第几个真实调用方 - 只有一个真实调用方的抽象默认不做除非用户明确要求 - 接口和基类的引入必须说明当前已知的差异化实现是什么 ## 模式使用 - 设计模式只在“当前需求”确实符合该模式场景时使用 - 禁止“先套模式再找需求”的写法 - 新引入的模式必须让代码行数减少或可读性显著提升 ## YAGNI原则 - NEVER 为猜想的未来需求预留扩展点 - 遇到“以后可能用到”的想法直接在注释或对话中说明“暂不实现”这条规则其实是很多AI代码复杂化的重灾区。AI特别喜欢建抽象因为它训练数据里的“好代码”都是这么写的但它没办法自己判断“这个抽象现在有没有用”。所以要逼它举证。第三条duplication.md# 重复代码约束 ## 三项判定 - 三段及以上完全相同的代码块必须提取公共函数 - 两段相似但逻辑存在差异的代码先尝试用参数合并合并不了再放弃 - 提取公共函数后调用方必须清晰可读禁止为了消除重复而制造更抽象的“魔法函数” ## 边界 - 不要为了消除重复把语义完全不同的两段代码强行合并 - 重复代码若只有两行三行且合并会让逻辑变难读则保持原样这套规则的核心思路是每个约束都对应一种复杂度失控的具体场景并且给了AI可执行的判断依据。AI不需要思考“这段代码是不是过度设计了”只需要对照规则逐条检查像一个拿着检查表的质检员。3.4 给Skill配一个度量脚本规则里提到圈复杂度、函数行数这些指标AI虽然可以大致估算但最好有一个精确的度量工具。我写了一个非常简单的Python脚本用AST解析代码文件输出核心指标。脚本我放在scripts/measure_complexity.py里长这样import ast import sys from collections import Counter def measure_file(path): with open(path, r, encodingutf-8) as f: source f.read() tree ast.parse(source) results [] for node in ast.walk(tree): # 只分析函数/方法 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): name node.name # 函数行数粗略包含注释 end getattr(node, end_lineno, node.lineno) lines end - node.lineno 1 # 嵌套深度最深层数 depth _max_nesting_depth(node) # 圈复杂度每个 if/while/for/except/and/or 算一个分支 cc _cyclomatic_complexity(node) results.append((name, lines, depth, cc)) return results def _max_nesting_depth(node): # 递归计算子节点最大嵌套层数略去细节 ... def _cyclomatic_complexity(node): # 统计分支节点数量 1略去细节 ... if __name__ __main__: for path in sys.argv[1:]: for name, lines, depth, cc in measure_file(path): flag if lines 50 or depth 3 or cc 10: flag -- 超限 print(f{path}:{name} 行数{lines} 深度{depth} 圈复杂度{cc}{flag})实际跑起来长这样$ python scripts/measure_complexity.py services/order.py services/order.py:create_order 行数54 深度4 圈复杂度17 -- 超限 services/order.py:apply_discount 行数12 深度2 圈复杂度4 services/order.py:notify_user 行数8 深度1 圈复杂度2有这份数据垫底AI说“这段代码复杂”就不只是感觉了。它可以精确告诉你“create_order函数54行圈复杂度17集中在两处分支逻辑建议拆成三个子函数。”这比“感觉有点复杂建议优化一下”有说服力得多。3.5 让Skill输出“先报告后动手”的工作流这个是我踩过坑之后总结出来的重点。最初版本的Skill让AI直接改代码效果很差。AI一上来就把整个文件重写了改动面巨大回归测试出了问题都定位不到源头。后来我把流程改成两阶段第一阶段只分析、只报告。AI读取相关代码和规则生成一份审查报告内容包括哪些函数超限、超限的具体指标、主要风险点、建议的简化方案。这份报告先给用户看让人拍板。第二阶段用户确认后AI才动手改。改的时候要遵循最小diff原则——只改报告里点名的代码不顺便重构周围无关代码。改完之后还要重新跑一次度量对比改动前后的指标变化。这个设计背后的逻辑是AI写代码不是问题AI低估“重构影响面”才是问题。让AI先出报告再过human review相当于把决策权留在人手里AI只负责分析和执行。到这里一个能用的Skill就搭起来了。整个过程不加新ideation就是把本来就该有的约束固化成AI能持续执行的机制。4. Skill 接管代码审查之后工作流是怎么跑的4.1 场景给现有项目加一个新功能拿一个具体场景演示一遍。假设你在一个订单系统里让AI加一个“订单超时自动关闭”的功能。没有Skill的时候AI可能这么操作新建一个OrderTimeoutManager类设计一套TimeoutPolicy策略接口再写一个定时任务调度器顺手把订单状态机也重构了。有Skill的情况下AI会先加载复杂度治理规则然后在动手前对相关代码做一次度量再给出类似这样的报告审查范围services/order.py 中的 create_order 函数 风险点 1. create_order 当前行数 38新增超时逻辑后预计突破 50 行 2. 订单状态判断已有两处 if 分支再增加超时判断后嵌套深度达 4 层 3. 当前代码没有现成的“超时时间”字段存储位置新增字段会牵连数据库迁移 建议方案 - 不新建管理类仅在 order 数据模型上加 timeout_at 字段 - 超时判断写成独立的 is_order_timeout(order) 函数不过度抽象 - 定时扫描逻辑复用项目里已有的调度框架不新造这份报告给用户确认后AI才开始动手。改动会控制在极小的范围内并且最后还会跑一次度量新增后的代码指标是否在阈值之内。4.2 让“复杂度指标”进入提交前的自检环节还有一个更省事的玩法把Skill挂在提交前自检流程上。让AI写完之后自己对着规则过一遍回答几个固定问题我写的函数都小于50行吗如果不是拆分的理由是什么有没有嵌套超过3层的分支能不能提前返回有没有引入没有真实调用方的抽象我的改动是否动到了需求之外的文件如果AI任一项不过关它应该停下来跟用户说明。实测下来这一步能提前拦住八成以上的“过度复杂提交”。很多AI生成的代码其实一开始就有问题只是被“编译通过”“测试绿了”掩盖了等代码合并到主干再想治理成本已经翻了十倍不止。4.3 加入“坏味道”示例让AI知道什么是不能做的参考样例是我在做Skill时觉得最有价值的一个部分。因为规则是抽象的AI能不能理解“抽象失控”是什么意思完全取决于它的训练语料。所以我在references目录里放了一组对比同一个需求一份坏写法一份好写法。坏写法节选故意示范什么叫提前抽象class NotificationSender: def __init__(self, channel: str): self.channel channel self._parser MessageParser() def send(self, message: Message): payload self._parser.parse(message) self._channel.send(payload) # 调用方 NotificationSender(channelemail).send(Message(hello))好写法节选同一个需求的最小实现def send_notification(message: str): email_client.send(message)AI只要看到这样的对比立刻就能明白规则想要强调的边界在哪里。这比你在规则文件里写一百句“不要过度设计”都管用。我现在强烈建议每个Skill都配一组正反样例尤其在代码治理这种“感性判断”占了不小的比例的场景。5. 常见问题与调试实录5.1 Skill管太紧AI连简单代码都不敢写了这是我第一个版本踩到的大坑。因为规则文件写得太密太严AI每次动笔之前都犹豫半天生怕哪个函数超过30行就被打回。有一次它甚至为了凑“单函数不超过30行”的目标把代码拆成七八个彼此窃窃私语的小函数整个文件读起来比原来更碎。后来我分析原因规则用词过于绝对AI把SHOULD当成了MUST。修法有两个。一是区分硬约束和软约束MUST类规则不引入无调用方的抽象、不做无关重构是底线SHOULD类规则建议30行、建议参数不超过3个是优化项允许在权衡后不遵守。二是降低触发频率只有新写代码或改动超过一定行数的场景才完整执行审查流程小改动走轻量检查。5.2 AI误伤“本来就复杂”的领域代码代码库里总有那么几个老文件圈复杂度天生高得离谱可能是历史遗留、可能是算法密集。Skill一跑AI就疯狂报警但你又不敢让它随便改这些文件因为每个改动都可能引发连锁问题。我的处理办法是给Skill加了一个“豁免清单”。在规则文件里允许配置COMPLEXITY_IGNORE_GLOBS比如## 豁免 - legacy/parser.py 这个文件历史原因复杂度高暂不治理只保证新增逻辑不恶化 - 所有生成器代码generated/*不做复杂度审计这样一来AI不会把精力浪费在改造历史代码上而是专注在控制新增复杂度上。治理复杂度的目标本来就不是“把所有老代码都改漂亮”而是“不让它继续变坏”。5.3 Skill分析消耗太多时间和上下文这是实操中非常现实的痛点。让AI每次都全量分析整个项目它很快就“忘”了前面的规则或者上下文被大量无关文件刷掉响应速度也会肉眼可见地变慢。解决办法是收敛分析范围。我后来把Skill的触发逻辑改成只分析用户指定要改的文件以及它的直接依赖不自动扫全库。另外分析报告要求精简输出控制在三百字以内只列超限函数和关键建议不贴大段代码。毕竟AI的核心价值是判断和生成不是给人类当格式化工具。5.4 AI报告说“要重构”但它重构完项目挂了这个问题我遇到得最频繁也是治理Skill很容易让人产生“这玩意儿不靠谱”印象的场景。原因在于AI对“简化”的理解往往是重写而不是小步调整。它把50行的函数拆成三个函数表面上指标好看了但内部状态传递全改了原本隐性依赖的地方全炸了。对策是加一条铁律重构必须保持行为不变并且必须附带可验证手段。Skill规则里我写死了一条——重构时先让AI说明它用什么方式证明“改完和改前行为一致”。有没有现成的测试能不能先跑一遍如果没有验证手段AI只能提出重构方案禁止自行落地。这里的核心逻辑是宁可保留一个丑但能跑的函数也不要换一个漂亮但炸了的函数。5.5 团队场景Skill约定和同事的代码风格打架有时候你辛辛苦苦把Skill配好了结果同事自己手写的代码风格和Skill的要求完全是两回事。AI按Skill标准给同事的代码提了一堆意见同事看了直摇头说你这是拿你的洁癖卡别人。这个问题的本质是Skill的规则默认是全团队的约定。要么把它放到项目级规则文件里让AI在任何时刻都遵循统一标准要么只在个人工作流里使用不去AI review别人的代码。我个人更推荐前者——复杂度约束本身是有普适性的只要阈值不是太激进绝大多数开发者都能接受“单函数别超50行”这类底线。我还整理了一个速查表方便遇到问题自行排查症状可能原因解法AI车道不敢下手SHOULD被当成了MUST区分硬约束和软约束降低触发频率老文件疯狂报警无豁免机制配置豁免清单忽略历史遗留代码响应越来越慢扫描范围太大限制只分析目标文件和直接依赖重构后测试挂掉缺少行为验证手段强制AI先声明验证方式再动手生成代码仍然复杂规则文件未被触发检查主描述文件中触发关键词覆盖是否足够6. 把它变成你的常用工具一点实战心得今天给到的整套方案是我在真实项目上迭代了好几轮才稳定的。最开始我只是写了个带阈值的提示词后来发现AI每次会话都失忆才把它固化成Skill。再后来发现光有规则不行AI看不懂什么叫“过度设计”于是加了正反样例。再往后发现AI改完代码经常影响原有的逻辑于是加了“先报告后动手”的工作流。每一次调整都是因为真实踩到了坑不是凭空设计的。还有几个我用下来觉得特别值得分享的细节放在最后一起说。第一个技巧把“复杂度是否达标”写进任务的完成定义。我们团队定义“一个功能算完成”不再只看测试是否通过还要看复杂度度量是否达标、是否引入了没有真实场景的抽象。这个定义一旦立住AI每次收尾都会主动跑一遍度量而不是写完整堆就交差。第二个技巧阈值从宽松开始迭代。第一次用不要一上来就卡50行、圈复杂度10这些中高阈值可以先设到80行、圈复杂度15让AI先适应“有规则”这回事。跑一两周后再逐步收紧。如果一开始就把弦绷得太紧AI和你都会很痛苦。第三个技巧小改动走轻量审查大改动走完整流程。让Skill根据diff行数自动切换模式改动小于二十行AI只需要自查有没有新增超限函数改动超过五十行就必须出完整报告。这样既不会因为过度流程拖慢日常开发速度也不会在关键重构时失控。我自己现在写代码已经不太依赖“哪个模型写代码更强”这种对比了。真正让AI从“越写越复杂”变成“越写越克制”的不是换更强的模型而是在模型外面给足约束、给足反馈、给足样例。一套好的Skill比换十个模型都实在。如果你也被AI的过度设计折磨过不妨花一个下午照着上面的结构搭一个自己的复杂度治理Skill然后用两周时间持续调阈值。等AI开始主动跟你说“这个抽象现在没有第二调用方建议不建”的时候你会觉得这笔投入非常值。
返回列表