编码感受和评估
在过去一周多的时间里,我在 ChatGPT 的协助下,生成了做一个简单编辑器的产品文档、技术方案文档,然后在这个基础上,进行程序的编码。
使用的工具纪要
为了更全面地感受 AI IDE 对研发过程的影响,我特意选择了一个我不太了解的项目,并且以整体形式做下来,从中深度感受 AI IDE 对我们开发过程的影响。
产品文档和技术文档生成的辅助工具:New Bing,The New Bing - Learn More
编程过程使用的编程工具:VScode + Cursor/Coplit,Cursor IDE
涉及的编程语言:JavaScript、CSS、HTML、Python、SQL
涉及的组成部分:前端、后台、数据库、Canvas 相关的图形处理接口
累计耗时:在过去一周多,断断续续地使用了 15 + 小时(包括生成产品和技术方案文档,有不少时间用于排查程序 Bug)。
代码规模:1500行+
个人使用感受记录
使用的第一感受
-
产生一种惊喜感,发现用它来做我所不了解的项目,也能对我形成比较大的帮助。
-
显著减少了我查询和搜索技术文档的时间,通过对话,能直接完成不少的工作。
-
是一种开发模式的变革,虽然现在还存在不足,但是,潜力无限!
关于研发效率
-
会有提升,但是比例不会很大,个人感觉 30%左右,因为生成的结果代码仍然需要进一步的详细 review 和测试。
-
在 prompt 描述非常详细的情况下,例如登录功能,它生成了详细的异常判断逻辑,这一块的生成非常快。当提供给它的 prompt 比较模糊时,则需要多次互动,而且未必能提供符合我们预期的代码。
关于 IDE 的选择(推荐 Cursor)
非常推荐使用Cursor,它不仅支持对话,而且能够选中指定代码,让它做逻辑修改,它会将修改记录反馈出来,问你是否 accept?并且,能够清晰看见它进行的代码改动。支持精细化控制,使用过程中,个人的强烈感受到,选中的范围越小、越细,它的结果越好,错误也越少。
如下图所示(我让它给button添加样式):
指定具体的函数,让它添加代码注释。
注释添加的结果:
关于它的不足之处(关键阅读项):
-
代码规模少的时候,例如只有 1 个文件,100 - 200 行代码时,它生成和修改都比较准确,当代码规模和文件逐渐变多,逻辑变多时,它的生成和修改变得有误,会遇到包括变量未定义、错删代码、没有考虑上下文逻辑等问题。缩小代码的选中范围、让prompt变得更详细能够一定程度上减少问题,但是,仍然会出现。
-
生成的代码中,偶尔会含有不存在的、但是命名很通用的样式、函数,它有时会默认认为你的上下文中存在这种“通用”函数,实际上并没有。
-
如果我修改的逻辑要同时改动多个文件,该功能并不支持,它目前只支持一个文件一个文件修改。不过,使用对话功能,它能以对话的方式告诉我们要在多个文件里修改代码,此时要我们手动复制和修改代码。
-
开发者对开发领域具体问题的认知仍然非常重要,如果开发者在prompt里没有提及,则它生成的代码也不考虑。例如,需要等待img.src的load完成后再重新刷新canvas画布,如果不提及,它的代码就是直接刷新画布,此时由于img并没有load完成,画布刷新的内容是空白的。
-
相比于生成文档、注释,程序代码对错误几乎是零容忍度的,一个未定义的变量就可以让整个程序完全终止运行,但是,生成文档有少量错误是不影响文档的阅读的,出版社对著作出版要求错误率达到万分之一。对于一个大规模程序而言,这个要求要高得多。
-
根据个人对 NLP 领域 AIGC 的理解,AIGC 本质是根据前文推理下文,模型精度越高,推理越准确,对于代码来说,则要求推理的准确度非常高,后面 2-3 代的 GPT 可能能解决该问题。
-
对话中,Cursor反馈结果以英文为主,偶尔才能看见中文。
简要总结:
-
短期看,GPT 还远不足以取代程序员,长期看,它的潜力无限,是一种趋势,随着它持续升级和迭代,预计它对研发效率的提升会越来越多。
-
我们应当拥抱 AI IDE,构造更详细、逻辑清晰的prompt,充分利用它的能力提升研发效率。
编码过程纪要(部分):
帮我生成一个基于VueJS的登录HTML页面,包含如下逻辑:
-
前置条件:用户已经注册过账号
-
后置条件:用户成功登录并进入图片编辑器页面
-
UI描述:见功能原型
-
流程/子流程/分支流程:
-
流程:用户在登录页面输入用户名和密码,点击登录按钮
-
子流程:系统验证用户名和密码是否正确,如果正确,则跳转到图片编辑器页面;如果不正确,则提示“用户名或密码错误,请重新输入”
-
分支流程:无
-
-
与其他子模块的接口:无
-
业务数据描述:
-
用户名:由字母、数字、下划线组成,长度为4~16个字符
-
密码:由字母、数字、特殊符号组成,长度为6~16个字符
-
-
边界值处理:
-
如果用户名或密码为空,则提示“请输入用户名和密码”
-
如果用户名或密码长度不符合要求,则提示“用户名或密码长度不合法”
-
修改为引用项目文件夹内的vue.js
登陆界面比较简陋,能帮我美化一下吗?
我想把页面元素居中
页面中的两个input没有对齐,帮我修改CSS
链接数据库操作
写一段Python代码,它的主要逻辑是读取用户表的账号密码,然后判断url请求传入的账号密码是否匹配,并且该python文件能以web服务形式运行。
相关信息:链接MySQL数据库,IP是111.230.249.101,port是20086,库名是editor。
表结构:CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(16) NOT NULL,
`password` varchar(32) NOT NULL,
`register_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
它的表编码为latin1,不正确,修改为utf8
帮我写一个web图片编辑器页面,包含以下功能:
-
前端使用HTML5的Canvas元素来实现图片编辑功能,Canvas可以支持图片的上传、裁剪、旋转、尺度变换等基本操作,并可以将编辑后的结果导出为一张图片
-
使用<input type="file">元素来让用户选择本地的图片文件,然后使用FileReader对象来读取文件内容,并将其转换为DataURL格式。
-
使用CanvasRenderingContext2D.drawImage()方法来将DataURL格式的图片绘制到Canvas上,然后使用CanvasRenderingContext2D.getImageData()方法来获取图片的像素数据。
-
使用CanvasRenderingContext2D.putImageData()方法来修改图片的像素数据,实现裁剪、旋转、尺度变换等操作。
-
使用HTMLCanvasElement.toDataURL()方法来将修改后的图片转换为DataURL格式,并使用<a download>元素来实现图片的下载功能。
-
上传或的效果,表现比较差:
修复x 报错
图片上传后,图片展示在canvas里变形了,请修复该问题。画布先定义为 1000*800
(AI 一直没有找到,是人工找出来的)
用div包起来,画布固定到页面的右侧
canvas画布支持上传多个图片,这些图片是共存的,并且,每个图片可以在画布中被移动.
这次它写得比较好,基本上只需要很少的修改就一步到位了。
这是上一条命令:canvas画布支持上传多个图片,这些图片是共存的,并且,每个图片可以在画布中被移动。在上一条命令的基础上,除了支持移动之外,还支持选中图片后的放大和缩小。
它提供的代码犯了一个小错,上下文理解不到位,变量名不对。(人工:手动修正)
在放大缩小问题上,它总是给我加这个事件,这个事件并不是我想要的,我想要的是稿定编辑器的那种效果。
但是,它添加的边框总是不生效,问了它多次,它也回答不出来。
人工发现,代码有 Bug,这句话让边框自动消失了。
比较正常的代码需要保存,AI 有时会把代码改乱,导致之前有的功能消失,例如,多张图片共存的能力。
在编辑器中,支持图片对象的选中和拖动
先问 new bing:基于canvas实现的web编辑器中,要实现选中一张图,出现一个边框,可以拖过拖拉边框实现放大和缩小,html和JS代码要怎么实现?
您好,这是必应。我尝试了一些网上搜索,发现了一些可能对您有用的资源。您可以参考以下链接12,它们提供了一些示例代码和解释,如何使用 HTML5 canvas 实现图片的拖拽和缩放功能。基本思路是:
当选中某个图片时:
-
在 canvas 上绘制图片,并在图片的四个角绘制可拖拽的锚点。
-
如果用户按下鼠标左键并拖动一个锚点,就根据锚点的位置调整图片的宽度和高度。
-
如果用户按下鼠标左键并拖动图片内部,就根据鼠标的移动距离移动图片的位置。
-
在每次鼠标移动后,重新绘制图片和锚点。
就当前阶段的 AI 而言,还是需要懂得代码,只是自己写的代码量变少了,程序员仍然有其价值。
添加缩放按钮,实现放大和缩小功能
AI 实现了这个功能,但是没有考虑保持图片原来的比例。
修正:
编辑器简单旋转功能开发
增加旋转后,坐标计算错误:
让它纠正,它一直都改不过来,一直错(后来我手动修正)。
创建后端服务代码(Python)
给当前代码增加跨域:放开cess-Control-Allow-Origin,允许跨域访问
from flask import Flask, request, jsonifyapp = Flask(__name__)# Connect to the MySQL database
mydb = mysql.connector.connect(host="111.230.249.101",port="20086",user="root",password="hello",database="editor"
)# Define a route for the login page
@app.route('/login')
def login():# Get the username and password from the URL requestusername = request.args.get('username')password = request.args.get('password')# Query the user table for the given username and passwordmycursor = mydb.cursor()sql = "SELECT * FROM users WHERE username = %s AND password = %s"val = (username, password)mycursor.execute(sql, val)result = mycursor.fetchone()# If the query returns a result, the username and password are validif result:response = jsonify({"message": "Login successful"})response.headers.add('Access-Control-Allow-Origin', '*')return responseelse:response = jsonify({"message": "Invalid username or password"})response.headers.add('Access-Control-Allow-Origin', '*')return responseif __name__ == '__main__':app.run()
能统一添加吗?这样每行都添加比较麻烦?
下文中的CORS(app)就实现了统一添加跨域头。
from flask import Flask, request, jsonify
from flask_cors import CORSapp = Flask(__name__)
CORS(app)# Connect to the MySQL database
mydb = mysql.connector.connect(
***
)# Define a route for the login page
@app.route('/login', methods=['POST'])
def login():# Get the username and password from the request bodydata = request.jsonusername = data['username']password = data['password']# Query the user table for the given username and passwordmycursor = mydb.cursor()sql = "SELECT * FROM users WHERE username = %s AND password = %s"val = (username, password)mycursor.execute(sql, val)result = mycursor.fetchone()# If the query returns a result, the username and password are validif result:return jsonify({"message": "Login successful"})else:return jsonify({"message": "Invalid username or password"})if __name__ == '__main__':app.run()
登陆成功后,给HTTP页面中进行set-cookie,设置username为对应的用户名。
当这个阶段,我将编辑器换成了 Cursor
把button的class变成light-button
它直接给我修改了代码:
在JS 代码中给input对象添加样式对象:
最终的效果:
按钮上下添加5 margin:
给按钮添加一个 width = 180 的约束:
给editor.html页面添加一个选中图片变换滤镜的功能
(事实上,没有支持选中图片)
产生了一行不对的代码,刷新的位置不对:
刷新的位置应该跟img当前的位置一致,不能是0,0开始:
存在一些问题:所有图片都变成了滤镜后的颜色
滤镜状态要保存在各个图片内部,而不是统一生效:
它又误删了一些代码,暂时不支持局部接受:
图片全部变灰的 Bug,它自身没能修复,但是,我 review 代码后,知道是因为 Filter 功能状态执行后没有保存对象到图片全局数据,我直接选中对应函数,让它把状态变更更新到数组中,问题终于被解决。
两个不同的prompt:
在 editor.html 中新增一个 Save 按钮,如果发现 cookie 中存在username用户名称,则将当前画布中 images 以username为key全部保存到IndexedDB中,并且支持通过给editor.html 传入username参数来获取存储在IndexedDB力的数据,并且还原到当前画布中。
在 editor.html 中新增一个 Save 按钮,如果发现 cookie 中存在username用户名称,则将当前画布中 images 以username为key全部保存到IndexedDB中,并且支持通过给editor.html 传入username参数来获取存储在IndexedDB力的数据,并且还原到当前画布中。并且,不能有语法错误,并且能够执行,不允许存在未定义的变量引用。
(后者可以规避 request 未定义的问题)
Cursor 生成了一大段代码给我:
它提供的代码执行有问题:
request未定义,报错了
让它修正:
通过username载入后的在画布上的图片对象,无法被选中和移动,请修正。并且,不能有语法错误,并且能够执行,不允许存在未定义的变量引用。
默认情况,无法修正。
载入的图片应该存入到全局的images数组中。
重新梳理描述:
你是一位资深的研发工程师,精通前端、数据库和Canvas相关特性,现在,你要基于当前代码的上下文,完成如下任务:保存画布上的图片元素到IndexedDB,并且方便后续以用户名取回来,还原到画布上。有如下要求:
1. 给editor.html的save按钮绑定一个事件,该事件需要保存全局变量中的images数据和画布内的图片数据。
2. 支持通过给editor.html 传入username参数来获取存储在IndexedDB的数据,并且还原到当前画布中,还原到画布的图片元素能够被移动、选中和执行其他编辑操作。
3. 程序不能有 Bug,要可运行,不能随意大幅度删改原来的存在的代码和影响原来的功能。
这段代码无法把图片在画布中渲染出来:
它建议我添加日志打印代码:
告诉它,仍然没有解决:
继续让我加日志代码:
最终找到问题原因了:image 的 src 尚未完成 load,提前执行了 Canvas 的 redrew
选中对应的函数,让它优化 redrew 的执行时间:
问题获得解决:
下载后的部分成果图:
(在这个过程中有很多交互,没有一一写出来)