Write code like a human will maintain it
Write code like a human will maintain it
像对待人类维护者一样去编写代码
One of the best things about LLMs is that they’ll write code for you, all day long. Who cares about DRY? You don’t have to be the one updating the same long conditional in four different files - the AI will just do it for you! Right? 大语言模型(LLM)最棒的一点在于,它们可以整天为你编写代码。谁还在乎 DRY(不要重复自己)原则呢?你不再需要亲自去四个不同的文件中更新同一个冗长的条件判断语句了——AI 会直接帮你搞定!是这样吗?
I’ve noticed myself letting it slide recently on a project I’d been building with AI. I needed the same access check in a handful of places: a route handler, a background job, an API endpoint, a webhook, etc. Each time, I’d describe what I needed, the model would generate something that worked, and I’d merge it. 最近我发现,在用 AI 构建的一个项目中,我开始对代码质量有所松懈。我需要在多个地方进行相同的访问权限检查:路由处理器、后台任务、API 接口、Webhook 等等。每次我只需要描述需求,模型就会生成可用的代码,然后我直接合并。
Each version looked roughly like this: if (user.isActive && user.hasPermission('read') && !user.isSuspended && account.status === 'open') { // do a thing } Essentially the same conditionals every time. Four conditions, maybe slightly different variable names, copy-pasted logic with a word or two changed.
每个版本看起来大致如下:if (user.isActive && user.hasPermission('read') && !user.isSuspended && account.status === 'open') { // 执行操作 }。本质上,每次的条件判断都是一样的。四个条件,可能变量名略有不同,逻辑就像是复制粘贴后改了一两个词。
There’s a much cleaner way to do this - a shared helper, for example, like something I’d extract if I were writing this myself. But I didn’t. The code worked! The tests passed and I wasn’t the one who’d have to touch it again. That’s the laziness here: if it doesn’t follow best practices, or I know a piece of code will be a pain to maintain, what difference does it make? When I need to change something later, the LLM deals with it, not me. 其实有更简洁的处理方式——比如提取一个共享的辅助函数,如果是我自己写代码,我肯定会这么做。但我没有。代码能跑!测试通过了,而且以后也不用我亲自去维护。这就是所谓的“懒惰”:如果它不符合最佳实践,或者我知道这段代码以后维护起来会很麻烦,那又有什么关系呢?反正以后需要修改时,是 LLM 去处理,又不是我。
Except the LLM doesn’t write in a vacuum. It reads your codebase. The files you have open, the patterns that are already there, and the recent changes you’ve made. Every shortcut you merge into your codebase is a signal about how things are done here. 但问题在于,LLM 并不是在真空中写作的。它会读取你的代码库。它会参考你打开的文件、现有的代码模式以及你最近所做的更改。你合并到代码库中的每一个“捷径”,都在向 AI 传递一种关于“这里代码该怎么写”的信号。
The next time you ask the LLM for another endpoint with the same access rules, the model won’t start from first principles. It’ll start from the other four copies already sitting in your repo. So you ask for a fifth endpoint, and you get a fifth conditional, with the same copied code. You ask for a refactor, and the model preserves all five, because that’s what your code looks like. The bad pattern isn’t a one-off anymore, it’s considered to be your style. 下次当你要求 LLM 再写一个具有相同访问规则的接口时,模型不会从底层逻辑出发,而是会参考你仓库里现有的那四个副本。于是,你得到了第五个接口,以及第五个包含重复代码的条件判断。当你要求重构时,模型会保留所有五个副本,因为你的代码库看起来就是这样。这种糟糕的模式不再是偶然,它被模型视为你的编程风格。
If you let things go on like this, can you really trust that the LLM will catch every instance if you try to fix it later? Sure, a few of these aren’t catastrophic. That’s how it always starts, but code smells do stack up. Each duplicated conditional, each “god” function, each “I’ll clean this up later” merge adds another layer of signaling to the next prompt. Eventually you can’t easily prompt your way out of it. At least not without getting your hands dirty and rolling up your sleeves. 如果你任由这种情况发展,当你以后想修复它时,你真的能相信 LLM 会找出所有实例吗?当然,几个这样的问题还不至于造成灾难。问题总是这样开始的,但代码异味(code smells)会不断堆积。每一个重复的条件判断、每一个“上帝函数”、每一次“以后再清理”的合并,都在为下一次提示词增加一层负面信号。最终,你将无法通过简单的提示词来解决问题,除非你亲自卷起袖子,动手重构。
The most frustrating part: I thought I was outsourcing maintenance to the LLM, but the slippery slope I found myself on was actually training it to have ever-worsening habits. Write code like a human will maintain it. LLMs are sponges that soak up everything you do and repeat it back to you. So make sure it’s good. 最令人沮丧的是:我以为我把维护工作外包给了 LLM,但实际上我正走在一条滑坡上,在不断训练它养成越来越坏的习惯。请像对待人类维护者一样去编写代码。LLM 就像海绵,会吸收你所做的一切并反馈给你。所以,请确保你喂给它的是高质量的代码。