我不喜欢跟机器人聊天
我有一部安卓手机,但我已经禁用了语音命令。我从未拥有过Alexa。我不太确定Siri到底能做什么,因为我不需要它给我找播放列表也能过活。所以当AI编程工具变得难以忽视时,我慢慢接受了。有一段时间,我采用了一种被称为‘一枪式工作法’的方式:像往常一样工作,偶尔将一大块工作委托给AI,然后等待结果。对于喜欢赌博的人来说,这真是个很酷的方法。
对我来说,部分障碍就是聊天界面本身。与模型对话的想法让我觉得愚蠢。我构建事物,我不与文本框协商。
但是规范驱动开发并不是这样工作的。你需要精确定义问题,告诉AI模型要考虑哪些文件,描述期望的结果,在AI生成一行代码之前,来回讨论计划。今天的AI模型在编码上很快,但在高层决策上仍然危险地不一致。当你与AI一起工作时,你的工作是将你的设计判断转化为书面规范,这样你就可以给AI一个低决策步骤的列表,这些步骤与你亲自编码时会做的相匹配。要想让那个规范正确,你必须进行协商。
过去六个月里,我学会了这一点。现在我已经相当擅长于此。我很少甚至从不手写代码,但我交付的功能却比以往任何时候都多。而且,由于我没有将软件设计推给模型,我坚持着在25年构建数字产品的职业生涯中所学到的同样质量标准。
我没想到的是,这会这么难教。
我现在是毕加索了吗?
有一种看法(可能是来自高级管理层?)认为大语言模型工具能提高所有工程师的生产力。在实践中,我并未看到这一点。我所看到的是,经验丰富、适应力强的工程师能够产出他们以前产量的数倍,而其他人则在产出相同或更少。
这对刚开始的人来说不是一个好状况。AI编程工具看起来很简单,因为它们的界面只是文本框。但你不会通过随意在空白画布上涂抹颜料就成为毕加索。
并行实现综合症
我关心初级工程师有很多原因,其中最重要的一点是,他们是我最喜欢一起工作的人!他们的开放性还没有消退,而且他们帮助我以不同的方式看待事物。最近,我和一位年轻的工程师一起进行了一次AI学习之旅,他聪明、对AI工具充满热情、渴望参与,并且毫不犹豫地与机器人交流。
然而,不知何故,它就是不起作用。
第一个迹象是身份验证系统。她需要为现有服务添加OAuth,于是她提示模型在一个庞大的规范中构建整个认证流程。登录、令牌刷新、会话管理、基于角色的访问控制,以及退出流程。所有功能。模型愉快地照做了。它生成了数百行代码,看起来完整,代码覆盖率达到100%,并且通过了测试。
但当我审查时,问题却是结构性问题。模型创建了一个新的会话存储,而不是使用我们已有的那个。它引入了一个与我们API网关冲突的令牌刷新模式。基于角色的访问逻辑复制了已经存在于共享中间件中的业务规则。从技术上讲,这些都没错——它确实能运行。但在架构层面,全是问题。
我和她坐下来,问了一个简单的问题:“在你提问之前,你知道我们今天是如何处理会话的吗?”她停顿了一下。“不完全是。”这就是差距。在要求模型在此基础上构建之前,她没有梳理系统架构。
下一次尝试有所不同,但还不够。她要求模型修改登录流程以支持一个新的提供者。这一次,她缩小了范围,这是好的。但她从她希望用户界面如何操作的角度描述了变更,而不是从现有代码已经处理的方式。模型没有理由知道我们现有的提供者抽象层,因此从头编写了一个并行实现。结果相同:可运行代码,错误架构。
我们讨论了发生的事情。我告诉她,把模型想象成一个团队里的新临时成员那样。有才华,速度快,但对我们的代码库零经验。你不会把项目交给一个临时成员就一走了之。你会说:这是我们的会话存储,这是中间件,这是添加新提供者的模式。把你的工作融入到这里面。
她理解了这个概念。但接下来的几轮让我意识到,概念只是简单的部分。她会从模型那里获得一个单一的计划,然后草率批准,不去寻求替代方案。她把模型愿意进行大量代码修改看作是彻底;我则视为不必要的风险。她还没有本能地说出“不,这个改变的影响范围太大了。”
有经验的工程师处理同样的OAuth任务时,会进行六到七个集中的轮次,每个轮次都限定范围于她已经理解的系统部分。
有经验的工程师会用六到七个聚焦轮次来处理同一个OAuth任务,每一轮都限定范围到她已经理解的系统的一部分。
软技能已成为新的硬技能
这可能听起来像是会抹去AI编码生产力增益的开销。但这并非新技能。经验丰富的工程师一直以来都在脑海里做这件事。区别在于,规范驱动开发将所有任务的软件设计规划外部化。过去在内部、在你和你自己的判断之间发生的设计协商,现在发生在聊天窗口中。
在你打开聊天之前,你需要知道你想要什么。不是实现方式,而是系统行为、约束条件、事物的形态。你需要足够了解你的代码库,以便告诉模型它的工作必须适配到哪里。当模型返回一个答案时,它很可能能运行。这还不够。你需要知道它是否真的适合你已有的系统。
有时候,你会意识到模型是正确的,而你是错误的,这时你需要谦逊来公正地评估这一点。
在整个过程中,区分善于使用这些工具的人和快速使用它们的人的关键因素是耐心迭代:愿意进行五到六轮的来回修改,而不是在第二轮就接受平庸的结果。还有不写代码而精确描述代码的技能,用通俗语言描述数据流、边界情况和故障模式。还有工程品味,这更难定义但易于识别:一种从长期维护角度看什么是好软件的感觉,而不仅仅是它能否运行。
然后,还有不舒服的部分。这些技能大多是模式识别,建立在多年来犯错的基础上。你能识别出一个糟糕的架构决策,因为你曾亲身体验其后果。你会质疑模型的首次回答,因为你曾实施过自己的第一个想法并后悔了。
初级工程师们正面临需要高水平进行设计协商的处境,但目前并没有相应的课程体系。是时候从“LeetCode”转向设计协商了,而这将是本系列未来文章的主题。
附录
软技能:与AI协商规格
在协商过程中
-
代码阅读流畅度: 扫描模型生成的代码,关注结构稳健性,而不仅仅是语法。你是在检查代码是否合适,而不是寻找错误。 为何重要: 模型可以生成有效的代码,但不属于你的系统。你需要快速发现这一点。
-
架构品味: 识别当解决方案在技术上有效但对于 这个 系统是错误的。 为什么重要: 模型不知道'对我们来说错误'是什么意思。你知道。
-
创意引导: 当模型卡住时,寻找替代框架。重新表述问题,提供类比,以不同方式约束它。 为什么重要: 模型会根据你如何框架事物来响应,更好的框架产生更好的结果。
-
知道何时采取立场: 模型会中立地呈现选项。您来决定,并阐述_为什么_一种方法对您的具体情况更好。 为何重要: 这就是工程判断力,需要多年才能建立。
-
建设性怀疑: 将模型的首次回答视为草稿,而非解决方案。并非愤世嫉俗,而是在接受前进行压力测试的习惯。 为何重要: 初稿输出具有诱惑力是因为它们流畅。流畅性不等于正确性。
-
知道自己不知道什么: 意识到模型可能正确而你可能是错误的。 重要性: 协商是双向的。有时它会带来你未曾考虑过的方法,你需要保持谦逊态度来公平评估。
在整个过程中
-
耐心: 来回五到六次,而不是在第二轮就接受平庸的结果。 为什么重要: 这区分了善于使用工具的人和只求快速使用工具的人。
-
技术沟通: 准确描述代码而不写代码。用通俗语言描述数据流、状态变化、边界情况和故障模式。 重要性: 这比大多数工程师想象的更难,并且它是你与模型之间的主要接口。
-
在头脑中承载复杂性: 追踪当前决策如何与模型未知的系统其他三个部分交互。 为何重要: 模型没有持久架构上下文。您就是工作记忆。
-
品味: 一种从用户角度感知优秀软件的直觉。不仅仅是“它能工作”,而是“这是否是正确的体验、正确的行为、正确的复杂度水平。” 为什么这很重要: 没有这个,你会接受那些功能上可行但实际上是错误的解决方案。
-
知道何时停止: 识别当规格说明足够好可以移交到执行阶段时,以及当你过度细化时。 为什么这很重要: 收益递减确实存在。在某个时刻,进一步的协商成本会超过其改善效果。
