再谈AI使用的一些思考。
思考
起因是今日操作系统课程设计下课后,与助教闲谈,他提到很多同学的归并排序代码merge中都用到了L和R数组,我顿觉一惊,因为这正是我用gpt生成的写法。当时瞅了眼感觉没什么问题,测试也没有问题,归并排序的板子我不记得也不想再查,索性交给ai去写了。
但这引发了我的一系列思考,似乎我现在的编程过于依赖AI了。诚然,现在gpt工具很发达,以我常使用的vscode为例,copilot处于侧栏可以随时提问解答问题,同时还有代码补全功能,tab键按几下基本上编程就结束了,最花费时间的反而是调试和测试,有的时候gpt产生的错误也是莫名其妙,不乏有低级错误的(虽然感觉也不算很低级)。
但是这样对吗?这样好吗?这是不是意味着我仍然处于缺乏代码能力呢?
“学计算机专业的学生只会用gpt辅助写代码,就好像专业潜水员只能用氧气管但不会憋气”
但不可否认的是,我们正在步入AI的时代,AI辅助编程绝对是大势所趋。那么对我而言,如何把握其中的度才是最重要的。我需要一个自洽的解释去指导我的行为。或者说,用更明智的方法在AI的辅助下提升自己的代码能力。
还是回到那个最老生常谈的话题。代码能力是什么?以我现在的理解来看,就是你能把想法通过代码变成现实的能力,可以是自己一行行手搓,也可以是使用prompt生成,但最重要的应该是你知道你在做什么。也就是,如果使用AI辅助生成的代码,你要知道它在做什么,你要知道它为什么这么做,你要有能够复现的能力(虽然大部分时候我做不到复现)。
我觉得面对我现在的困境,一个值得尝试的方法是,自我code review。也就是完成项目后,自己回头再来审视,夹杂着自己完成与gpt生成/remake的代码,看看自己是不是真的理解自己在做什么。但虽然大一写大作业被CR了很多回,我却没有CR过别人,也就是其实也不太清楚应该做些什么,不妨请教一下deepseek。
以下摘取几轮回答中我认为关键可实施的部分:
自我Code Review
核心关注维度
代码质量(微观层面)
可读性:命名规范(如 calculateRevenue() 优于 calc())、函数长度(建议不超过 20 行)
健壮性:边界条件处理(空集合、极大值、并发竞争)
异常处理:是否遗漏错误捕获(如文件 I/O、网络请求)
测试覆盖:单元测试是否覆盖核心路径和边界情况
设计合理性(中观层面)
单一职责:类/函数是否专注单一功能(如将数据解析与存储分离)
扩展性:新增功能是否需要修改已有代码(违反开闭原则)
依赖管理:是否存在循环依赖或过度耦合(如模块A同时依赖模块B和C)
架构一致性(宏观层面)
规范遵守:是否符合项目定义的架构模式(如 MVC、DDD)
技术债务:是否引入临时解决方案(如 // TODO: 需要重构)
第三方依赖:新引入的库是否经过技术评审
安全与性能
注入风险:SQL/命令/模板注入漏洞
资源泄漏:未关闭的数据库连接/文件句柄
算法复杂度:是否存在 O(n²) 等低效实现
可维护性
文档完整性:关键算法是否有设计说明
配置管理:硬编码参数是否抽离为配置项
日志追踪:关键操作是否有日志埋点
流程汇总
阶段一:全局认知(15分钟)
目标:建立项目全景图
操作步骤:
- 绘制模块地图
用纸笔画出模块依赖关系:[主入口] → [核心业务模块] → [数据层] ↘ [工具类] → [第三方库]
- 标注重灾区
用荧光笔标记:- 近期频繁修改的代码(易出问题)
- 包含复杂算法的部分
- 涉及外部资源操作(文件/网络)的区域
阶段二:架构审查(30分钟)
核心问题清单(自问自答):
模块化
❓“如果删除这个模块,系统能否正常运行?”
❓“新增一个数据源需要改动多少文件?”依赖关系
❓“工具类是否被业务逻辑直接依赖?”
❓“是否存在A模块导入B,B又导入A的情况?”设计原则
# 示例:检查单一职责原则 class ReportGenerator: def load_data(self): ... # ❌ 数据加载不属于生成器的职责 def analyze(self): ... def export_pdf(self): ...
❓“这个类承担了多少种职责?哪些可以拆分?”
阶段三:代码深潜(60分钟)
1. 核心算法审查(25分钟)
操作方式:
- 对复杂算法 手动演算:
// 示例:二分查找实现 int search(int[] nums, int target) { int left = 0, right = nums.length - 1; // ✅ 正确初始化 while (left <= right) { int mid = left + (right - left)/2; if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; // ❓当nums=[5], target=3时是否死循环? } return -1; }
- 用具体案例(如空数组、极值)逐步推演执行过程
2. 关键路径审查(20分钟)
检查点:
// 示例:订单处理流程
function processOrder(order) {
validate(order); // ✅ 验证前置
const payment = charge(order);
if (payment.status !== 'success') { // ❌ 未处理支付中的状态
throw new Error('支付失败');
}
saveToDB(order); // ❓数据库异常如何处理?
}
- ❓“如果支付接口超时会发生什么?”
- ❓“数据库保存失败后是否有回滚机制?”
3. 防御性审查(15分钟)
破坏性测试清单:
- 输入
null
/undefined
- 传递空数组/空字符串
- 注入超大数值(如
Integer.MAX_VALUE + 1
) - 模拟网络断开/文件不存在
阶段四:交叉验证(30分钟)
1. 时间差验证
- 第一天晚上写代码
- 第二天早晨重新阅读(获得新视角)
2. 角色扮演法
角色 | 审查重点 | 示例问题 |
---|---|---|
新手程序员 | 代码可读性 | “这个变量名tmp 指什么?” |
系统运维 | 故障应对 | “这个异常会导致服务崩溃吗?” |
产品经理 | 需求符合度 | “这段代码实现了哪个需求项?” |
3. 逆向工程测试
根据代码 反推需求文档 ,对比是否符合原始设计:
代码行为:用户年龄小于18岁时自动拒绝注册
原始需求:需家长同意的年龄阈值应为16岁
👉 发现业务逻辑偏差
阶段五:总结固化(15分钟)
1. 问题分类记录
## [日期] 审查报告
### 严重问题
- 文件:`PaymentService.java` 第82行
- 问题:支付超时无重试机制
- 风险:直接导致订单丢失
### 优化建议
- 将配置阈值从代码迁移到配置文件
### 知识收获
- 学习到递归算法的栈溢出防护方法
2. 建立个人检查表
□ 所有循环都有终止条件吗?
□ 是否处理了第三方API的失败情况?
□ 关键函数是否有参数校验?
高效心法
20-50法则
- 每20行代码至少提出1个问题
- 每50行代码发现1个实质性缺陷
三遍阅读法
- 第一遍:快速通读,掌握结构
- 第二遍:逐行推敲,质疑每处设计
- 第三遍:模拟运行时,跟踪数据流
致命问题清单
遇到这些情况 必须重构:- 函数超过30行
- 出现重复代码块
- 存在未处理的
TODO
注释
该流程可在 2-3小时 内完成对1000行左右代码的深度审查,无需任何工具辅助。记住:好的自我审查就像与代码对话,不断追问 “如果…会怎样?” 直到所有可能性都被覆盖。
总结
使用AI关键在于“知道自己在做什么”,要始终保持掌控力而非被动接受。自我 code review是提升这种掌控力的方式之一,其“高效心法”部分是重中之重。