LLM对程序员的冲击和影响( 二 )


如果以上四点再叠加上大型软件的规模效应,其中包含软件系统本身的规模和软件研发团队的规模,问题就更严重了,即会显著提升软件研发过程中的沟通成本、决策成本、认知成本和试错成本,而这些才是软件工程问题的本质,这些本质问题自始至终都没有变过,LLM 对此也基本无能为力 。
基于上述的分析,我们可以看到,软件工程的核心矛盾并没有改变,现代软件工程应对的是规模化场景下的各种问题,基于 LLM 实现的编程提效只是其中的一小部分,而其中最重要的需求和代码演进模式都没有发生本质变化,我们接下里分别展开讨论一下 。

LLM对程序员的冲击和影响

文章插图
4 需求的重要性没有变,在 LLM 时代还被放大了  
只有我们的需求足够的清楚,那么生成的代码才会准确 。如何准确全面描述需求成为了关键 。面向自然语言编程,首先你要有能力把话讲清楚 。但是问题是:你能讲清楚吗?
我们通过一些实践发现,要把需求描述到让它能正确写出来代码,需要的工作量似乎已经接近甚至超过编码了 。为什么会这样,有两个方面的原因 。
一是因为大多数的代码实现是 imperative 的,而需求描述是 declarative 的,这两者对人的要求完全不一样 。我们程序员群体接受的教育是编程,而不是需求描述,也就是说程序员本来更擅长写代码,而不是描述需求 。
二是因为在当前的开发模式下,程序员直接用代码默认帮需求(产品经理)做了很多代偿 。很多在需求中没有明确提及的内容被程序员用代码直接实现了(代偿) 。而现在要倒过来先把需求的细节完全理清楚这个可能不是程序员当前的工作习惯 。而且代码的信息熵其实要大于自然语言,程序员更善于用代码而非自然语言来描述事务 。
举个例子:如何清楚地描述一个排序函数 sort 的需求?sort 输出的数字必须是从小到大排列的,这样描述需求就够了吗?其实远远不够,重复数字怎么处理?排序数据的数量有没有上限?有的话如何提示?排序时长需要有超时设计吗?是预先判定还是中途判断?算法复杂度有明确的要求吗?算法需要应对并发吗?并发的规模怎么样?等等 。
一个软件的需求,不仅仅是功能性的,还有很多非功能的需求,这些都是需要描述清楚的 。另外代码实现的时候,还要考虑为可测试而设计,为可扩展而设计,为可运维而设计,为可观测而设计等等 。原本这些很多是开发代偿了,现在要从需求生成代码,你必须要提前讲清楚 。
所以,我们的总结是:“软件从业者高估了编程的复杂度,但是却低估了功能和设计的深刻度” 。
5 代码是持续“生长”出来的,需要持续更新  
对于现行的软件开发范式,当需求发生变动后,一般是会在原有代码基础上改动,而不是直接从头全量生成全部代码,这个时候,LLM 本质上做的是局部编程的辅助(pAIr programming) 。局部编程辅助过程中,经常需要对代码做局部修改,而这个往往并不容易 。
我们知道,代码的信息熵大于自然语言,用信息熵更低的自然语言去描述代码,尤其是准确描述大段代码中的若干个位置往往是困难的 。想象一下,如果只用在线聊天的方式跟别人讲在代码的什么地方做修改的效率是何其低下,相比指着屏幕,或者使用专门的 CR 工具,效率的差距是巨大的 。
如果需要进一步描述如何修改就会更困难,因为大概率需要用到很多代码上下文的相关描述,所以对于 prompt 的表述要求以及长度要求都很高 。
另外,LLM 接纳修改意见(prompt)后的输出本身也是不稳定和不收敛的,同时也具有不可解释性,LLM 本质上不是基于修改意见(prompt)进行改写,而是基于修改意见(prompt)重新写了一份 。输出的代码需要人重复的阅读和理解,使得认知成本变高了 。
同时,LLM 的原理决定了其会“一本正经的胡说八道”的本质,会混合捏造一些不存在的东西,可以说人工智能的混合捏造是人工智能在无知情况下的“自信”反应,而这个点在代码生成上是灾难性的,比如会将不同类型的 SQL 语句混在一起使用,会分不清 Go 语言的 os.Kill 和 Python/ target=_blank class=infotextkey>Python 语言的 os.kill() 。这个问题可能需要使用 AI 审计 AI 的方式来缓解了 。
刚才提到,要在原有代码基础上修改,就需要利用已有的代码上下文,而不是从 0 开始 。要实现这一点,一个最朴素的做法就是把整个项目的代码都贴到 prompt 里,但这样并不现实 。因为 GPT-3.5 限制最多只能 4096 个 tokens,GPT-4 最多 8192 个,除非项目非常小,否则根本放不下 。这个问题可能需要用 langchain 来解决了 。


推荐阅读