摘要
1) 一句话总结 在 AI 编程助手时代,开发者的核心职责已从编写代码转向审查代码,需通过掌握“代码异味”等核心软件工程概念,来引导 AI 生成可维护且符合业务上下文的生产级代码。
2) 关键要点
- 角色转变:开发者的主要职责已从“编写代码”转向“审查代码”,实质上成为了指导 AI(初级开发者)的高级开发者。
- 核心概念:掌握代码异味(Code Smells)、抽象和设计模式等概念,是避免盲目复制粘贴 AI 输出的关键。
- 代码异味定义:指脆弱、易隐藏 Bug 且让人或 AI 难以理解真实意图的代码结构。
- 异味一:发散式变化(Divergent Change):模块承担过多职责(如将数据检索、工程和模型构建耦合)。AI 常因盲目执行“添加功能”的指令而将代码硬塞入现有类中,加剧此问题。
- 应对发散式变化:重构为“编排器(Orchestrator)”模式,将不同阶段的复杂性分离到独立类中,以提升可测试性和架构灵活性。
- 异味二:夸夸其谈未来性(过度泛化):为未知的未来需求过度设计。强大的 AI 结合模糊提示词极易生成数百行不必要的模块代码。
- 应对过度泛化:遵循“YAGNI(你不需要它)”原则,从最简单的可行方案开始,让代码库有机迭代生长。
- 上下文决定架构:代码架构取决于业务背景。生产环境需要解耦的“编排器”,但在 MVP(最小可行性产品)或研发场景下,过度解耦反而是一种异味,简单的“全能型”类可能更合适。
- 开发者的核心价值:AI 缺乏完整的业务背景,开发者的价值在于利用“结构化直觉”引导 AI,将精力集中于解决问题和架构方案,而非编写样板代码。
3) 风险/缺口
- 操作与崩溃风险:当一个类扮演多个角色时,每次修改都有引入 Bug 的风险,崩溃几率成倍增加。
- AI 上下文污染风险:AI 可能会将不同领域(如数据清洗与模型训练)混为一谈,为了适应某一端的变更而错误修改另一端的逻辑。
- AI 放大 Bug 风险:AI 能在瞬间重写跨领域的数百行代码,大幅增加引入单元测试无法捕获的 Bug 的几率。
- 失控与维护风险:过度泛化会导致代码脆弱,且未来如果需要撤销 AI 预先做出的过度设计,过程将非常痛苦。
- 效率伪命题(不可扩展):试图将所有业务细节写成冗长的提示词喂给 AI 是不可扩展的,这仅仅是将“写代码的体力劳动”变成了“写提示词的体力劳动”。
正文
AI 可以编写代码,但你必须掌舵。掌握相关核心知识,才能让你在 AI 时代保持竞争力。
本文不探讨如何构建提示词(Prompt)来让 AI 施展魔法,而是探讨如何在现代 AI 编程生态中保持开发者的核心价值。我们将介绍现有的软件工程实践概念,帮助你更好地利用编程助手,而不是盲目地按 Tab 键或复制粘贴代码。
通过了解这些概念,你将清楚在自动生成的代码中需要注意哪些常见陷阱,并知道如何引导编程助手创建可维护、可扩展的生产级代码。无论你是初学者、毕业生,还是希望提升编程技能的其他技术领域专业人士,这些内容都将使你不仅能更好地使用 AI 助手,也能成为更优秀的开发者。
核心概念的转变
我们将涵盖以下高级概念:
- 代码异味(Code Smells)
- 抽象(Abstraction)
- 设计模式(Design Patterns)
对经验丰富的开发者来说,这些概念早已成为第二天性,是在多年的代码审查和 Debug 中刻入大脑的直觉。而现在,随着编程助手成为所有级别开发者的必备工具,这些概念比以往任何时候都更加重要。
为什么?因为编写代码的体力劳动已被卸载。任何开发者的主要职责已经从“编写代码”转向了“审查代码”。每个人实际上都成了指导初级开发者(即编程助手)的高级开发者。因此,即使是初级软件从业者也必须具备审查代码的能力。在当今行业中蓬勃发展的,将是那些具备高级开发者远见的人。
本文将首先深入探讨第一个主题:代码异味(Code Smells)。
什么是代码异味?
“代码异味”是一个非常贴切的术语——就像发酸的牛奶提醒你不要喝它一样。
几十年来,开发者通过反复试错,总结出了哪些代码能够长期有效。“散发异味”的代码通常是脆弱的、容易隐藏 Bug 的,并且让人类或 AI 代理难以理解其真实意图。
在过度依赖编程助手(例如使用 AI 构建从专业机器学习流水线到移动应用的各种项目)时,通常会出现两种典型的“代码异味”:
- 发散式变化(Divergent Change)
- 夸夸其谈未来性(Speculative Generality / 过度泛化)
异味一:发散式变化(Divergent Change)
当一个模块或类同时处理太多事情时,就会发生发散式变化。代码的目的“发散”到了许多不同的方向,它试图包揽一切,而不是专注于做好一项任务(违背了单一职责原则)。这会导致代码经常崩溃,并需要因为各种独立的原因进行修复。
AI 时代为何频发?
当开发者不熟悉代码库并盲目接受 AI 的输出时,极易陷入这种困境。即使你使用了结构良好的提示词,当你要求 AI “添加处理 X 的功能”时,它通常会完全照做,将代码硬塞进现有的类中,尤其是在现有代码库已经很复杂的情况下。如果你不从整体上考虑代码的角色、职责和预期用途,最终很可能会得到散发异味的代码。
机器学习工程示例与风险
假设有一个 ModelPipeline 类,同时处理以下事务:
- 数据检索: 如果我们开始有多个数据源(如 BigQuery 表、本地数据库或 Azure Blob),代码需要修改。
- 数据工程: 如果上游数据或下游建模发生变化,代码需要修改。
- 模型构建: 如果我们使用不同的模型(如 LightGBM 或神经网络),上游建模逻辑需要修改。
将平台、数据工程和机器学习工程耦合在一个地方,我们让这段代码被修改的理由增加了两倍——这就是典型的“发散式变化”异味。
这会带来什么问题?
- 操作风险: 每次修改都有引入 Bug 的风险。一个类扮演三个角色,崩溃的风险就增加了三倍。
- AI 上下文污染: AI 会将清洗和训练代码视为同一个问题的一部分,可能会为了适应数据工程的变更而进行不必要的训练逻辑修改。
- AI 放大风险: AI 可以在一秒钟内重写数百行代码。如果这些代码涉及三个不同的领域,引入单元测试无法捕获的 Bug 的几率就会大幅增加。
如何修复?
将原本包揽整个数据科学技术栈的类,重构为一个“编排器(Orchestrator)”。编排器负责协调不同的建模阶段,而每个阶段的复杂性则被干净地分离到各自独立的类中。
这样做的好处:
- 最小化操作风险: 关注点解耦,职责极其清晰。你可以放心地重构数据加载逻辑,而确信机器学习训练代码保持原样。只要输入和输出(“契约”)保持不变,影响下游的风险就会降低。
- 代码可测试: 测试范围更小且定义明确,更容易编写单元测试。
- 乐高般的灵活性: 架构易于扩展。需要从 S3 迁移到 Azure?只需引入一个新的 AzureBlobLoader。想尝试 LightGBM?直接更换训练器即可。
异味二:夸夸其谈未来性(Speculative Generality)
“发散式变化”通常发生在庞大且复杂的代码库中,而“夸夸其谈未来性”则多见于启动新项目时。这种异味是指开发者试图通过猜测未来的发展来“面向未来”编程,结果引入了不必要的功能,徒增复杂性。
比如,你本打算构建一个简单的 LightGBM 分类模型,却花大量时间让流水线支持所有类型的模型、交叉验证和超参数调优方法,甚至加入了人工反馈机制。最终导致工作量巨大、代码脆弱,且偏离了最初的简单需求。
AI 时代为何频发?
最新、最高效的编程助手最容易产生这种异味。如果将强大的 AI 与模糊的提示词结合,你很快就会得到过多的模块和数百行新代码。这些代码乍看之下可能很合理,但 AI 实际上是在为你尚未规划的未来做出设计选择。这会让你感觉失去了对代码库的控制,未来如果需要撤销这些设计,将会非常痛苦。
核心原则:让代码库有机生长
在审查 AI 输出时,请牢记“YAGNI”(You ain’t gonna need it,你不需要它)原则。这表明你只应实现当前需要的代码,而不是你预见未来可能需要的代码。
从最简单的可行方案开始,然后不断迭代。这是一种更自然、更有机的代码库增长方式,既能完成任务,又能保持精简,减少 Bug。
场景与权衡:没有绝对的“正确”
软件工程很少有绝对“正确”的代码,一切都取决于上下文(Context)。
在前面的例子中,我们认为将“全能型”类重构为“编排器”是必要的,前提是假设这是生产级代码,需要频繁维护和添加功能。但如果这是用于新产品 MVP(最小可行性产品)或研发(R&D)的代码呢?
在这种情况下,选择复杂的“编排器”模式反而可能是一种“异味”。如果项目范围仅考虑一个数据源或一个模型,构建三个独立的类和一个编排器就属于“预先解决你尚未遇到的问题”。因此,在部署考虑尚不明确且有特定输入/输出要求的 MVP/R&D 场景中,简单的“全能型”类可能更合适。
核心启示:如何在 AI 时代脱颖而出
编程助手可以写出功能和语法都完美的 Python 代码,但它不了解你的完整业务背景。它不知道它正在编写的脚本是一个一次性的实验,还是价值数百万美元的生产流水线重组的核心。
效率的权衡 你可能会说,我们可以把每一个业务细节都喂给 AI。但在实践中,这是不可扩展的。如果你必须花半小时写一份“背景备忘录”才能得到一个干净的 50 行函数,你真的提高了效率吗?还是仅仅把写代码的体力劳动变成了写提示词的体力劳动?
你的核心价值 在 AI 时代,数据科学家的价值已经发生了根本性的变化。编写样板代码、格式化和单元测试的工作将由 AI 接管。为了在盲目复制粘贴的同行中脱颖而出,你需要具备结构化直觉,引导编程助手朝着符合你独特业务场景的方向前进。这能带来更好的可靠性、性能和结果,从而彰显你的价值。
要做到这一点,你需要通过了解代码异味,以及后续文章将深入探讨的设计模式和抽象等概念,来积累这种源于多年经验的直觉。最终,这将为你腾出更多精力去专注于解决问题和架构方案——这才是数据科学真正的乐趣所在。