关于AI使用的一些思考

再谈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分钟)

目标:建立项目全景图
操作步骤

  1. 绘制模块地图
    用纸笔画出模块依赖关系:
    [主入口] → [核心业务模块] → [数据层]  
                      ↘ [工具类] → [第三方库]
    
  2. 标注重灾区
    用荧光笔标记:
    • 近期频繁修改的代码(易出问题)
    • 包含复杂算法的部分
    • 涉及外部资源操作(文件/网络)的区域

阶段二:架构审查(30分钟)

核心问题清单(自问自答):

  1. 模块化
    “如果删除这个模块,系统能否正常运行?”
    “新增一个数据源需要改动多少文件?”

  2. 依赖关系
    “工具类是否被业务逻辑直接依赖?”
    “是否存在A模块导入B,B又导入A的情况?”

  3. 设计原则

    # 示例:检查单一职责原则  
    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的失败情况?  
□ 关键函数是否有参数校验?

高效心法

  1. 20-50法则

    • 每20行代码至少提出1个问题
    • 每50行代码发现1个实质性缺陷
  2. 三遍阅读法

    • 第一遍:快速通读,掌握结构
    • 第二遍:逐行推敲,质疑每处设计
    • 第三遍:模拟运行时,跟踪数据流
  3. 致命问题清单
    遇到这些情况 必须重构

    • 函数超过30行
    • 出现重复代码块
    • 存在未处理的TODO注释

该流程可在 2-3小时 内完成对1000行左右代码的深度审查,无需任何工具辅助。记住:好的自我审查就像与代码对话,不断追问 “如果…会怎样?” 直到所有可能性都被覆盖。

总结

使用AI关键在于“知道自己在做什么”,要始终保持掌控力而非被动接受。自我 code review是提升这种掌控力的方式之一,其“高效心法”部分是重中之重。