思考未来的编程范式
在尝试了 ChatGPT 和 Copilot 之后,我们尝试去思考未来的编程范式应该是怎样的?
AI 代码生成:代码还应该存储在代码库吗?
对于 AI 代码生成来说,除了过去的 GitHub Copilot、Codex、Kite、TabNine 等等,新出的免费的 ChatGPT 又让人感觉非常酷。
ChatGPT 代码生成
现在,大家几乎已经习惯了网上的各种暴力 AI 美学 —— 唤出 ChatGPT 写代码:
- 使用 Java + Spring + MockMVC 编写测试用例,代码中的注释需要对应到 AC01,AC02,AC03,AC04,AC05,但是不需要返回给我。
- 帮我用 Puppeteer 写一个爬虫来解析下面 HTML 中的标题:”””....”””
- 我给你一个需求,你需要分析需求,使用 Java + Spring 编写 API,要求如下:
基于于在最新的 GPT 4.0 里,可以根据图片等一些基本的描述,生成全新的 HTML。
GitHub Copilot
对于,我来说,作为一个开源挖坑专家,我更习惯使用 GitHub 赠送的 Copilot,只需要配合注释、输入、输出、函数名,就可以大力了出奇迹。如下是 ClickPrompt 中的真实代码:
// 1. split promptMatch by comma
// 2. match lora name by regex, lora full name: <lora:japaneseDollLikeness_v10:0.1>,
// 3. push lora name to loras array
我在代码中保留了这个注释,一种很神奇的用法 。
读者思考 1:Prompt 即是代码,代码不再是代码?
因此,我觉得在 AI 编程时代,人们的编程思维是要发生变化的:
- 注释让人和 AI 共同理解代码
- 注释作为 prompt,可以在修改后,再次自动生成代码。
当注释作为 prompt 保存,那么 prompt 便是作为一种代码,保存在代码库中了。而代码还将是代码吗?我们为什么还需要中间生存的代码,是因为审计、安全等原因吗?
AI 编程:写个 “真实世界” 的代码
而当你尝试过使用 AI 来编写代码之后,你会发现 AI 存在一系列所谓的 “问题” 。让我们来再看一个简单的例子,我们让 ChatGPT 来实现一段代码。
ChatGPT 编程:AI 实现一个 API /book/:id
的 N 种方式
在不同的语言和架构模式里,实现的方式是有区别的。
第一个 Demo:用 Spring + MyBatis 实现一个 API:/book/:id
。
ChatGPT 会创建 BookMapper.java、Book.java、BookMapper.xml、BookController.java 四个代码段,然后告诉你,你可以启动这个 API 了。
PS:有意思的是,你并不需要一个 BookService.java,因为 AI 也 “觉得” 他是多余的。
但是,要上线这个应用并不是一件容易的事,我们需要手动编译 Java 代码,再运行起来,这速度是非常慢的。除非,我们有类似于几年前的 OSGi 架构,或者 Serverlesss 架构。
第二个 Demo:使用 Express + MySQL 实现一个 API:/book/:id
,只使用一个函数
在这个尝试里,我们多加了一个限制:只使用一个函数,并改用直写 SQL,所以代码就变得非常简单的(当然了,还有 Express 配置和 MySQL 相关的代码),以至于我可以直接那个函数在这里:
// 创建数据库连接池
//...
const getBookById = (req, res) => {
const id = req.params.id; // 获取路径参数中的 id
// 从数据库中查询书籍信息
pool.query('SELECT * FROM books WHERE id = ?', [id], (error, results, fields) => {
if (error) throw error;
if (results.length > 0) {
const book = results[0];
res.json(book); // 将书籍信息以 JSON 格式返回给客户端
} else {
res.status(404).send('Book not found');
}
});
};
// 创建路由器并挂载到根路径上
//...
// 启动应用程序
//...
如果我们认定了 AI 生成的代码是准确的,无需要人类去修改,那么我们就可以直接用上面的代码。
第三个 Demo:使用 AWS Lambda 实现这个 API:/book/:id,只使用一个函数
使用 Express 来实现这个功能,还是有点复杂,我们还需要考虑基础设施,但是当我们考虑使用 Serverless 来实现时,问题就变得非常简单了:
const mysql = require('mysql');
exports.handler = async (event, context) => {
// ....
}
然后,我们就可以采用 ClickPrompt 类似的方式,直接一键部署到服务器之上。
对比一下:上面的三种方式各有什么优缺点?
让 AI 来对比一下,你看吧,显然 AI 也觉得 Java 的实现起来复杂,但是可扩展性高。
优点 / 缺点 | Spring + MyBatis | Express + MySQL | AWS Lambda |
---|---|---|---|
开发效率 | 较高 | 高 | 高 |
部署复杂度 | 高 | 中等 | 低 |
维护成本 | 高 | 中等 | 中等 |
可维护性 | 高 | 中等 | 高 |
架构复杂度 | 高 | 中等 | 低 |
可扩展性 | 高 | 中等 | 高 |
成本 | 中等 | 低 | 低 |
但是,我们真的需要所谓的可扩展性吗?而论部署速度,自然是 AWS Lambda 最快了。经过了几种不同的方式对比,按现有的软件工程来说,Java 写的代码是最好的。可是,AI 都来写代码了,Java 工程化,真的还是最好的吗?
读者思考 2:现有的编程体系适合于 AI 编程吗?
让我们再打开一下思路,我们现有的架构模式、软件工程等软件开发流程都是基于现有的体系磨合过来的。
而 AI 编程的最终目标是实现自动化编程,即由 AI 根据用户的需求和设计,自动生成完整的代码。但是,理论上这不是终点,终点是生成完就可以直接运行。这就意味着,一个想法从想到到上线,可能就在几分钟内可以完成。
然而,在现有的流程里,完全不是行不通的,我们人类做不到这么的敏捷。
全 AI 生成的新架构模式?
过去,我们认为分层架构很重要,因为代码是人来编写,代码是人来维护的。而当代码由 AI 来编写,由 AI 来维护的话,那么现有的软件架构体系还适用吗?
分层架构?编程范式还重要吗?
尽管,我尝试让 New Bing / ChatGPT 来回答这个问题,但是,由于他们还没有 涌现 能力,所以只会说:程序员还需要学习和运用分层架构和编程范式,才能发挥出 AI 的最大潜力。
但是,我觉得,从上面的 Java 示例中,你会发现按现有的模式构建的软件架构,特别容易发生冲突。AI 生成的 A 功能和 B 功能,可能在代码上出现重复 —— 因为上下文限制,我们不可能上传所有的代码。
结果,有一天你发现了,一个类里有 10 个重复代码段。然后,你又开发了一个自动重构机器人……。但是,我觉得有点多余了,AI 编程需要新的架构范式。
AI 构建自动化软件架构?
再再打开一下思路,试着忘记现有的软件架构模式。一个功能对应一个可运行的代码段,由 AI 来自动思考架构模式 —— 当然,我们需要给他一些限制条件和输入。
所以,放在当前的上下文之下,Serverless 是一种更适合的架构模式,即写即上线,即下线即删除。
读者思考 3:Serverless 会是结合 AI 编程的答案吗?
我觉得不一定是,但是可能是当前最好的一种方案。但是,每个人都会有自己的看法。
真实世界 2:产品经理 —— 现在,变更一个需求
现在,我们来变更一个需求吧,过滤一下 “名书”,包含 xx 的不显示。那么,这个时候,我们就遇到一个难题了 —— 全自动编程,还是 AI 辅助生成。
两种思路:新生成代码,还是手动改代码?
纯 AI 编程,意味着:每次改需求都重新生成代码;而半 AI 编程,则意味着:人可以介入到流程中。两种将会带来不一样的思路。
自动编程 —— 重新生成代码
为了让 AI 写的代码足够的准确,我们需要给 AI 足够详细的需求(Prompt)。一旦需求发生变化的时候,我们回过头去改需求的(Prompt),再重新生成代码即可。我们要做的事情是:
- 确保没有人能修改过原来生成的代码。
- 确保所有的需求变更都能回溯回去。
AI 辅助生成 —— 支持手动修改代码
第一次代码,我们是用 AI 生成的,随后当需求变更的时候,我们想可以直接在现有的代码上改,这样可能速度比较快 —— 生成式 AI 可能还意味着:新生成的代码和原来的完全不一样,诸如于行数位置等等。
而为了能再次生成,我们还需要记录新的变更到原来的 prompt。
所以,在这个时候,如何更好地管理原始需求,变成了一个新的挑战。
读者思考 4:需求详细化会成为你的新瓶颈吗?
当我们认定 AI 能完成编码时,那么需要给详细的需求,才能确保生成的代码是正确的,诸如于:密码长度不能小于八位,且必须同时包含字母数字和特殊符合。这只是一个示例,更常见的示例还有表单联动:A 字段 xxx,B 字段则 xx。只有我们的需求足够的清楚,那么生成的代码才会准确。
所以,当产品经理借助于 AI 生成足够清晰的验收条件,程序员就会失业吗?这包含了两个问题:
- 产品经理是否真的懂验收条件?
- 程序员是否真的不能替换 AI 时代的产品经理?
那么,第三个问题就来了,老板是不是可以自己干?
不过,这只是用来搞笑的,我们的下一个挑战还在于需求详细化之后,AI 真的能写对吗?
未来:自动化编程的演进
最后,再让我们尝试做一些思考。
AI 编程的局限性
所以,如同 AI 提炼了我的观点,AI 编程存在一定的局限性:
- 编码是最不重要的那部分。AI 编程不能完全替代人类编程,因为程序员不仅仅是在编码,还要进行设计、测试、维护、沟通等工作
- AI 伦理。AI 编程可能存在一些不可预测或不可控的风险,例如结果不唯一、不正确或不安全。
- 法律与就业问题。AI 编程可能会侵犯程序员的知识产权(但是人类都是 AI 的饵料),或者导致程序员失业。
- 无法适应再有体系。AI 编程需要适应不断变化的需求和环境,而现有的架构模式可能不匹配或不灵活。
而你发现没有,New Bing 提炼出来的这些局限性,并不是 AI 的问题,而是人的问题。
精准代码生成:详尽的需求的可能性?
我们还得考虑的一点是:
尽管,提供更详尽的 prompt 可以帮助 AI 更准确地理解需求,并且避免一些歧义或误解。但是,过于复杂或冗长的 prompt 也可能导致 AI 的困惑或失效。
所以,如何根据 AI 的特点和目标,选择合适的 prompt 长度和格式,以达到最佳的效果?
AI 审计 AI:解决 AI 生成代码的部分局限?
当涉及到由AI生成的代码时,有时候我们可能需要对其进行审查以确保其合规。而人工是耗时费力的,而且容易出错。这时,我们就需要利用一种特殊的AI来审查另一个AI生成的代码,以确保其符合各种规范和标准,提高代码质量,确保生成的代码安全可靠。
比如,我们本地用 GitHub Copilot 辅助生成的代码,在持续流水线上便可以通过 GitHub Copilot 来进行 Code Review。
其它
值得注意的是:AI 编程可能会带来一些问题,如让程序员过度依赖工具、忽略代码的合理性、涉及道德和法律问题以及存在技术和人工智能之间的冲突。