AI写代码越来越复杂?用KISS和YAGNI原则驯服AI生成风格
2026/9/5 5:59:37 网站建设 项目流程

1. 先搞清楚一件事:你说的“朴实易读”在工程里叫什么

其实每次有人到我这儿来问“怎么让AI写得朴素一点”,我第一个反应都是:你先把你自己要什么,用工程的词汇翻译一遍。因为如果你自己都说不清什么叫“朴实”,那AI、agent、哪怕是人,都没法替你执行。

你描述的这种需求,在软件工程里是有明确说法的。它不是玄学,也不是审美偏好,而是一整套早就被前人总结过、踩过坑、写成原则的东西。

最常见的几个说法:

  • 可读性优先(Readability First):代码是写给下一个维护者看的,不只是写给编译器看的。在代码评审里,读代码的时间远多于写代码的时间,所以“一眼能看懂”比“写得聪明”更有价值。
  • KISS原则(Keep It Simple, Stupid):用最简单的方式完成任务。不是不能用复杂方案,而是当简单方案够用时,不引入额外复杂度。
  • YAGNI(You Aren't Gonna Need It):不为“万一以后用得上”的需求提前做设计。很多人让AI越写越复杂,就是因为在对话里不停追加“顺便支持一下”“预留一个扩展位”,结果生生把一个两天的需求拱成了两周的架构。
  • 最少惊讶原则(Principle of Least Astonishment):一个函数的命名、行为、返回值都应当符合调用者的直觉。看到getUser()就应当返回一个用户对象,而不是可能返回null、可能抛异常、还可能顺便改库。
  • 低耦合高内聚(Low Coupling, High Cohesion):模块之间依赖少,模块内部关系紧密。本质上也是为了能单独看懂一块,而不需要把整个系统都装在脑子里。
  • 防御式编程的反向警告:很多程序员以为“防御式编程”就是每个函数开头都加一堆空值判断。实际上,真正的防御式编程是区分“内外部边界”的,在外部边界做防御,在内部逻辑保持清爽。让AI在所有地方都做防御,代码就会变成一堆if。

有意思的是,当你跟AI agent说“写得简单一点”,它不一定能理解。但当你跟它说“遵循KISS原则、避免防御式编程滥用、优先使用显式辅助函数拆解逻辑、去除YAGNI视角下不需要的抽象”,它是可以理解的——因为这些概念在它的训练语料里出现次数足够多,它知道对应的代码形态长什么样。

所以,传达需求的第一步不是研究怎么给agent下指令,而是先把你的风格要求翻译成它认识的原则词汇。

2. 为什么AI越写越复杂?根子往往不在模型,在你自己

我在带团队做AI辅助开发的时候,观察到一个特别常见的现象:一个人工智能agent写的代码,一开始还挺干净,但聊到第20轮的时候,代码已经膨胀成了一个带工厂模式、策略模式、还有事件总线的微框架。

这时候大家的第一反应是怪agent“过度设计”。但你把整个对话记录拉出来看,你会发现里面至少出现过五六次这样的输入:

  • “可不可以顺便支持一下XML格式?”
  • “以后如果换数据库怎么办?加个抽象层吧。”
  • “这块逻辑后面可能被多个地方复用,先封装成服务。”

每一条你看起来是“提问”或“讨论”的话,agent全部都解读成了需求。因为大语言模型的对话机制本身就是:你说什么,它就把它当作上下文约束。

这就是YAGNI原则在AI协作里最容易失效的场景。

真实工程里,如果你跟一个同事说“以后可能换数据库”,你的同事会问“现在要换吗?不换我就不做”。但agent不会问,它会非常热心地帮你在今天就把未来十年的架构演进全部安排上。因为它没有成本概念,它不知道多出来的这几百行抽象,未来三个月里每一次改需求都要跟着一起改。

那怎么治?

我自己在实践里折腾出一套很管用的方法:给agent设立“复杂度预算”

什么意思?就是你在需求里明确告诉它,允许使用哪些结构、禁止使用哪些结构、最多允许几层抽象。这比空泛地说“保持简单”要扎实得多。

举个例子,我在项目说明文件里会写一段这样的话:

本项目的代码风格要求:

  1. 优先使用普通函数和显式数据传递,禁止引入IOC容器、事件总线、动态代理等机制,除非任务说明中明确要求。
  2. 单个函数禁止超过40行,超过时必须拆分为多个语义明确的辅助函数。
  3. 禁止在数据访问层之上再包一层Repository,直接用领域服务调用数据访问对象。除非有多数据源需求并已显式声明。
  4. 不要添加项目未要求的配置项、配置文件、扩展点、接口抽象。
  5. 每个文件顶部用三行以内注释说明“这个文件干什么、被谁用、边界在哪”。函数体内尽量少写行内注释,能用命名说清楚的事就不要用注释解释。

每次agent看到这一段,产出的代码风格都会明显收敛。它仍然会有想发挥的冲动,但至少有了边界。边界不是靠模型自觉守住的,是你反复在需求里强调、在代码评审里纠偏出来的。

另一个特别隐蔽的复杂度来源,是“让AI解释代码”。

很多程序员写完代码之后,喜欢让agent“解释一下这段逻辑”,agent就会用一堆架构词汇把代码讲得好像一个分布式系统。你听完觉得,哇,原来我写的这个模块这么牛。然后你就舍不得简化它了。这其实是语言的自我实现预言。代码本身可能只有十行,agent的解释却在暗示它的设计很精妙。

所以我一般要求团队成员:要解释,就解释“这个函数在什么条件下被调用、读写了哪些数据、返回什么”,不要解释“这是基于某某模式设计的”。如果agent对代码的解释需要大谈设计模式,那基本说明这代码设计得太复杂了。

3. 把风格要求传达到位的四种渠道,不只是“提示词”

很多人以为把需求传达给AI agent就是写好对话开头的那一段提示词。其实,真正规范的软件工程环境里,风格约束的传递至少应该有四个渠道。

3.1 第一层:项目级规范文件,先让agent“读得到”

现在主流AI编程工具普遍支持在项目里放一个类似AGENTS.mdCLAUDE.md.cursorrules之类的项目记忆文件。不同的工具叫法不一,但逻辑一样:新会话开始的时候,AI会自动读取这个文件里的内容,把它当作环境上下文的一部分。

这里有个关键认知:这个文件不是写给agent看的“魔法咒语”,它本质上是项目干系人对项目的共同假设

你团队里每来一个新人,第一周会问什么问题?项目结构是什么、依赖怎么加、代码风格什么标准、哪里是核心逻辑不能乱动。这些信息以前靠口头传,后来靠wiki,现在应该落到这个文件里。

我在自己的项目里会分四个区块来写这份文件:

  • 项目目标和边界:这个项目解决什么问题、明确不做什么。
  • 技术栈和结构约定:允许用哪些框架、目录怎么组织。
  • 代码风格标准:长度限制、命名规范、抽象层级限制。
  • 工作流程约束:改代码之前先列影响范围、单次修改不超过哪些模块、测试要覆盖什么。

这四部分的信息密度远大于你每次对话前临时想出来的提示词。而且它对所有新会话生效,不需要你重复讲。

3.2 第二层:任务级提示词,每轮对话都要有“人话约束”

光有项目规范文件还不够,因为agent经常会“看了但不执行”。大模型对长上下文的注意力天然会分布在对话靠后的位置。如果项目文件是三千字的长文,而你的最新需求是“把那个按钮的颜色改一下”,它很可能就只顾着按钮,忘了前面关于代码风格的长篇大论。

所以我习惯在每个任务提示词的末尾,都附加一个简短的“本任务约束”尾巴。哪怕只是重复一句“保持改动范围最小,不重构无关代码”。

经验上,这句话写在“任务描述之后”比写在“任务描述之前”效果更好。原因也挺直观的,新指令替代旧指令的倾向,在最新位置上的指令权重更高。

3.3 第三层:示例驱动,给它看“这就是我要的样子”

讲再多抽象原则,不如给它看一小段你认可的代码长什么样。agent对模式识别的能力远强于对规则推理的能力。你给它一段“符合本项目风格”的函数示例,它就会以这个示例为模板去生成其他函数。

这个技巧我用了很多次,效果出奇地好。

有一次我在项目里推“纯函数优先”的风格,团队里有个agent动不动就想在模块里搞单例对象、搞全局状态。我在那个仓库的约束文件里放了一个大约18行的纯函数示例,这个示例没有什么高深技巧,就是输入一个状态对象、返回一个新对象,完全没有副作用。从那以后,agent生成的代码里,纯函数的比例大幅上升。

为什么?因为示例在视觉上比一堆文字规则更容易被模型“抄作业”。规则是抽象的,模型需要在规则和代码形态之间做一次映射;示例是具体的,模型可以直接对齐,你给什么画风,它就能临摹出什么画风。

3.4 第四层:评审闭环,让风格问题“被看见”

很多个人开发者在用AI编程的时候,缺少一个环节:代码评审。

在团队协作里,代码评审是风格约束的最后一道关卡。一个人写得再自由,只要有另一个人类在评审时打回来说“这里过度设计了啊”,风格就不会彻底跑偏。但个人用AI,没有这个评审者,于是agent生成的代码就会在几十轮迭代里逐步失控,而且没人拦得住。

我有段时间是一个人维护一个开源项目,用AI辅助写了不少代码。后来我自己给自己定了个规矩:每个merge请求合入前,必须用一句话回答自己——“如果这段代码突然坏了,我能不能在五分钟内定位到问题所在的文件和函数?”如果答案是“不能”,那不管它跑起来多正常,我都会让agent重写,拆成更小更直白的函数。

这一段经历让我意识到:AI编程工具真正改变的不是“写代码”这个动作,而是把“代码评审”这个原本属于团队的环节,推给了每个独立的开发者。你能不能守住风格,取决于你有没有一个可执行的审查信号,而不是取决于你反复看了多少遍。

4. 手把手教你怎么写agent的开发约束文档

这部分上点硬货。我把近一年在多个项目里用过的项目规则文件核心片段摘出来,逐段解释为什么要这么写。你直接参考着改就能用。

4.1 技术栈与约束声明,先划边界

一个典型的问题:你项目用的是Spring Boot,agent有时候会帮你引入一个你没用过的工具库。为啥?因为它觉得“这个功能用XX库很方便”。但每个新依赖都是有维护成本和安全风险的。

约束文件里我一般这么写:

本项目运行环境是Java 17 + Spring Boot 3.x,数据库是PostgreSQL。除非本任务明确说明,禁止引入任何新的第三方框架、工具库或中间件。所有需求都应优先使用现有依赖实现。

这段的用意特别直白:不让它加东西。agent在候选方案里看到某个功能用现有依赖也能实现,只是代码稍微多几行时,它通常会反过来觉得“多几行就多几行吧,总比加依赖强”。

4.2 代码风格量化,让“朴实”变成可以校验的指标

刚才提到“可读性”这个词,但可读性本身没法自动检查。你需要把它拆成可以执行的指标。

我目前用下来比较好用的一套约束清单:

## 代码风格红线 1. 最大嵌套深度不超过四层,超出必须提前返回或拆分函数。 2. 单函数不超过50行(空行不计算在内),超出必须拆分。 3. 相同结构的逻辑必须抽取公共函数,禁止出现两段结构相同但内容散落的代码。 4. 禁止全局可变状态,如有状态共享需求,通过方法参数显式传递。 5. 命名不得使用常见缩写和拼音缩写。临时变量可以短,但公开方法名、参数名、数据库字段必须完整单词。 6. 能不创建类就不创建类,普通函数解决不了的时候再考虑封装。 7. 异常处理分两种情况:外部输入或IO操作需要显式处理;内部纯计算逻辑不吞异常,能早抛就早抛。

这条清单本身不是从什么教科书里抄的,是相当多维护场景试出来的。你别小看这套数字,它给agent一个非常确定的判断标准。它不需要去理解什么叫“简洁”,只需要对照着“是否超过50行”“是否嵌套超过四层”来检查自己的输出。

4.3 需求变更的版本约束,对抗需求蔓延

agent的对话历史越长,越容易把早期的临时想法当成最终需求。这是上下文衰减带来的一个很难避免的现象。

所以我在比较大的项目库里,会再追加这么一段:

如果本任务是从既有需求上扩展而来的,请先列出原有实现的关键逻辑,再说明本次新增部分的改动点。如果本次改动会波及超过三个文件的既有逻辑,先停下来,向用户列一个“影响面清单”,等待用户确认后再动手写代码。

这段规约的实际效果是:把“是否要大改”这个决策权重新拿回人类手里。agent不是不能做大规模重构,但做大规模重构的决定,应该由人来下。agent默认应该干的是最小化改动。

4.4 提交与解释模板,把风格约束延伸到提交说明

代码提交说明是一个极容易被忽视的风格约束落点。agent提交的commit message往往非常“AI腔”,什么“feat: 优化若干功能”“refactor: 完善项目结构”,你根本看不出它改了啥。

我在约束文件里会写:

commit message 必须包含以下三要素:本次改动解决的问题、核心改动点、涉及的主要文件。禁止写“优化”“完善”“增强”这类没有信息量的动词。

这个约束还有一个额外的作用:它是代码评审前的自检。agent如果写不出一个清晰的commit说明,大概率说明这次改动是模糊的。强制它写出“改了什么、为什么改”,某种程度上会让它在动手之前先思考清楚。

5. 实操中的提示词模板,直接就能抄

下面这几个提示词模板,都是我在多个编程辅助场景中反复调整过的。你根据自己的实际任务微调就能用。注意,不是让你把它们拼接成一段超长提示词一次性甩给agent。经验上,分段、按阶段给,效果更好。

5.1 任务启动时,用“场景式”语境开场

很多人的提示词第一句就是“帮我写一个XX功能”,这个开场略掉了太多约束。我习惯换一种方式:

我正在维护的项目是一个长期运行的后端服务,代码会被后续多位同事迭代维护。请你帮我实现用户注册接口。项目已有的技术栈是XX,项目规范文件是AGENTS.md,开始写代码前先通读该文件,并先按我下面的约束输出方案,经我确认后再写正文。

注意这里的关键词是“长期运行”“后续多位同事迭代”。为什么这么讲?因为agent缺少场景感。你告诉它“这个代码会被维护十年”,它就会更偏向可维护性;你告诉它“这是一个一周后可能要删掉的MVP验证脚本”,它就自然愿意写得更快更糙。给它场景,就是给它选择风格方向的依据。

5.2 方案评审时,用“反问式”逼它做减法

当agent输出一个明显过度复杂的方案时,硬要它“改简单点”往往不如用反问来引导。我会用这一组问题:

请先回答我三个问题再写代码:

  1. 这个方案相比最简单的实现方案,额外增加了哪些类、接口或配置?
  2. 每增加一项,分别是为了解决当前哪个具体需求?如果现在不解决也不会影响当前功能,请删掉它。
  3. 方案里的抽象模型,在未来三个可预见的迭代里,是否真的会被复用?如果不存在明确复用点,请不要引入。

这一步的本质是倒逼agent进行“需求回溯”。agent会发现自己很多的架构设计其实并没有对应的需求来源,只是在按概率采样的“常规最佳实践”。你多问几次,它就会变得克制得多。

5.3 需求迭代时,用“冻结范围”提醒

在继续对话前,如果你感觉上下文已经很长了,先做一次“范围冻结”:

下面我们会进入新一轮迭代。请先忘记之前所有未被当前需求覆盖的讨论,只保留已经实现的代码作为上下文。现有代码里,不要因为本次需求的引入而主动改动与需求无关的部分。如果后续有改动需要涉及无关部分,请先单独列出,经确认后再做。

这个提示多次让我避免了“改一行需求,全项目被重构”的悲剧。你可以当成一个安全锁来使用。

5.4 复现风格示例时,用“对照法”描述

给示例的时候,光给代码不够,最好再给一段描述性的对照:

请注意,示例代码的好处在于,它没有过度使用设计模式、没有任何装饰性的封装、所有函数都是显式传参。请用同样的风格实现下面的功能。如果新功能没有示例中的对应结构,请用示例中最接近的结构来仿写,不要自创新模式。

为什么强调“仿写”?因为我发现agent在见到示例后,仍然可能因为新功能而“放飞自我”,自作主张换一套它更熟悉的模式。明确要求它“用已有结构仿写”,能把它固定在当前项目的风格轨道上。

6. 常见“风格失控”的现场与排查,全是真实踩过的坑

光写好规则文件还不够,执行过程中一定会遇到各类失控情况。我挑几个高频的场景分享下排查思路,你以后遇到了至少知道往哪个方向检查。

6.1 无论怎么强调,它还是写出一堆“毕业设计”代码

场景重现:你明确说了“保持简单、不要抽象”,结果agent还是给你生成一个AbstractFactory配合Builder再加Strategy的多层结构。

这类情况的根因通常不在agent,而是你要求它实现的功能本身没有明确边界。人话说就是“需求不清晰”。功能一旦模糊,模型为了兜住所有可能性,就会选择最通用的架构形态。

排查动作:先别急着怪agent,花五分钟把需求里所有“尽量”“可能需要”“多种场景”这类模糊词给删掉,把需求改成“单一入口、单一流程、返回单一结果”的明确形式,再让它重写一遍。大多数情况下,代码复杂度会当场掉一半。

6.2 一改旧代码就“顺手优化”,改出一堆风格不一致

你只是让它加一个字段,它把整个类都重写了。这是agent缺少“改动边界感”的典型表现。

排查动作:去看它重写前的代码是不是有风格问题。比如原来有大量的重复代码、废弃注释、长函数,agent在接触代码库之后产生了“我应该顺便清理干净”的判断。

严格来说这个动机是好的,但对大型项目来说,这种行为等同于一个新人入职第一天就重构核心模块。要在规则里明确说:禁止在任务实现过程中重构与任务无关的代码,如有重构建议,可以单独整理成“建议清单”,不能直接执行。

6.3 agent喜欢给自己留后门技巧,写一堆看似聪明的元编程

你让它处理某些重复性的样板代码时,它可能会使用动态代理、eval执行、反射调用、动态拼接等等“魔法技巧”。它觉得这样代码量少、通用性强,但对维护者来说,这类代码的调试成本极高。

排查动作:这种场景我在约束文件里不会泛泛说“不许用魔法”,而是会明确点名禁止项:

禁用语法:反射、eval、exec、动态生成源代码并编译、通过字符串拼接方式动态调用方法、隐式类型转换的黑魔法。

注意,不是所有项目都绝对禁止反射。但如果你追求的是“朴实易读”,那反射这个技术本身就属于“高级用法”,它带来的灵活性和它造成的理解成本通常是等量的。新人接手的时候,一句“这代码为什么能跑起来”能耗掉半天时间。禁止反射可能损失一些编码灵活性,但极大的降了维护时的认知负担。

6.4 上下文太长之后,规则逐渐“失忆”

这是最让所有用agent开发的人头疼的问题。项目等级越高,对话越长,早期的代码风格约束越容易失效。模型不是真的“失忆”,而是当上下文中积压了太多关于业务逻辑的讨论之后,那些“风格问题”占的权重被压低了,模型更倾向满足最新、最具体的指令。

排查动作:如果一段工作会话已经超过大概二三十次的往来,而且你明显感觉到agent的风格开始飘了,不要犹豫,新建一个会话,重新加载规则文件,然后把当前的需求压缩成一段简报贴进去,继续工作。别想着在旧会话里硬掰回来,掰不回来了。

我自己的使用习惯是:每次新开会话前,都会把旧会话中关于需求和约束的关键结论手动整理成一个几行的“会话简报”,在下一个会话开头粘贴。这样既保持了上下文的连续性,又主动丢弃了几十轮无关讨论。

这个技巧看上去很原始,效果却是惊人的好。

7. 把“风格”当工程资产来经营,而不是当聊天参数

走到这一步,你会发现一个很关键的认知转变:编程风格约束在一开始可能只是一种“提示词写作技巧”,但长期来看,它应当是工程资产的一部分,是需要被反复维护和更新的配置文件

这意味着什么呢?

首先,你的规则文件需要定期迭代。我大概每隔两三周会回看一次自己的agent约束文件,看看哪些规则真的起作用了,哪些规则已经形同虚设,有没有新踩出来的坑需要补进去。这个过程,和你在项目里重构一个核心模块没有任何区别。

其次,规则文件需要版本管理。千万不要把它当成一个随手写的备忘录。我们团队的做法是规则文件和代码在同一个仓库里管理,改动也要走PR评审流程。如果有人觉得某条规则不合适,可以提出来讨论。经过讨论沉淀下来的约束,比某个人拍脑袋写出的一条提示词要坚实得多。

不要小看这些“软性约定”的价值。很多项目最后做不下去,绕不开一个历史原因——代码在多次需求和人员的变迁中失去了统一的可读标准。维护者每打开一个文件都要花很长时间理解作者的思路,变更成本指数级上升,直到没人敢动那套代码,项目就僵死了。

而你说“编程风格朴实、不要复杂化、强化可读性”,本质上是在对抗这个死亡螺旋。

我踩过几次坑之后的体会是:别指望AI agent天生理解你的品味。品味是一种高度个人的东西,模型的默认输出是“大多数人的平均品味”,而平均水平往往意味着中庸中带一点炫技的冲动。你得通过规则、示例、评审闭环,把你的品味一点点“教”给它。

你教得越具体、越系统、越像对待一个真正的工程需求那样对待风格约束,agent给你的回馈就越好。你只是随口说一句“写简单点”,它也只能给你一个随口做出来的简单。

反过来,当你在项目文件里写下“这个项目不要引入无谓的框架,不要预测不需要的未来”,然后用一轮又一轮的代码评审去校准它的时候,你会慢慢发现,AI辅助编程这件事,本质上就是把你自己变成一个更严格、更清醒的工程管理者。你没法当甩手掌柜,但你可以用更低的成本,把手底下的AI变成一个风格稳定、不飘不浪的可靠成员。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询