我花 7 天用 AI 重构了我的开发方式:一个 Java 程序员的 AI 工作流实践
摘要:这不是一篇“AI 一键写完项目”的爽文,而是一份可以照着执行的 Java 开发工作流复盘。我用 7 天把需求澄清、读代码、写接口、排查故障、补测试、优化 SQL 和沉淀文档重新串了一遍。结果不是每天少上四小时班,而是把大量无效搜索、机械搬运和重复解释压缩掉,让有限的四小时高质量时间真正落在设计、验证和决策上。文中给出任务日志、提示模板、Java/JUnit/SQL 示例、踩坑记录和一套可复用的 AI 协作清单。
先说结论:AI 没替我写代码,它先替我消灭了“来回切换”
过去我对“AI 提升开发效率”这句话一直有点警惕。
网上常见的演示是:输入一句需求,模型吐出几百行代码,页面一刷新,项目就跑起来了。这个过程看着很爽,但只要把场景换成一个维护多年的 Java 项目,事情立刻变了:
- 表名不是按常规命名的,字段里还有历史兼容逻辑;
- 一个“新增状态”可能同时影响枚举、数据库、缓存、消息队列和前端字典;
- 单元测试能通过,不代表集成环境里的事务和权限没问题;
- 真正耗时间的往往不是敲代码,而是确认“改哪里、为什么改、改完会不会伤到别处”。
所以这次我没有给自己定“7 天学会 20 个 AI 工具”的目标,也没有统计模型生成了多少行代码。我只记录一件事:完成同一类开发任务,从收到需求到交付可验证结果,中间哪些时间可以被压缩,哪些责任必须留在人手里。
一周之后,变化最明显的不是打字速度,而是工作节奏。
以前遇到陌生模块,我会在 IDE、数据库客户端、浏览器、群聊记录和旧文档之间反复切换。现在我会先把任务边界、相关文件、失败现象和验收条件整理成一个上下文包,再让 AI 做第一轮归纳。它负责快速展开可能性,我负责删掉不符合项目事实的部分;它负责生成候选修改,我负责看 diff、跑测试、查数据和做最后判断。
这套协作可以概括成一句话:
把 AI 放进反馈闭环,而不是放在交付终点。
一、改造前:一天 8 小时是怎么被切碎的
我先连续记录了几天普通开发日,没有刻意挑“适合 AI”的任务。下面是一个典型工作日的时间去向。它不是严谨的生产力实验,只是帮助我定位浪费发生在哪里。
| 工作环节 | 原来的常见耗时 | 真正困难的部分 | 是否适合交给 AI |
|---|---|---|---|
| 理解需求、补问题清单 | 50 分钟 | 找到模糊条件和隐含边界 | 适合做第一轮审查 |
| 阅读陌生模块 | 90 分钟 | 找入口、调用链、数据流 | 适合归纳,人来核对 |
| 编写接口与 DTO | 80 分钟 | 项目约定、校验、异常语义 | 可生成草稿 |
| 排查报错 | 110 分钟 | 从噪声日志中找关键证据 | 很适合压缩日志和提出假设 |
| 补测试 | 60 分钟 | 边界用例是否完整 | 适合生成测试矩阵 |
| 写文档、提交说明 | 50 分钟 | 把改动讲清楚 | 适合结构化整理 |
| 沟通和上下文切换 | 40 分钟 | 反复解释同一背景 | 可用任务记录减少重复 |
这里最值得优化的不是“编写接口”那 80 分钟,而是上下文切换和重复理解。
比如一个空指针异常,我可能先复制完整日志到搜索引擎,再打开报错行,再向上追调用者,再查数据库里是否有脏数据。中途同事问一句进度,我又要把思路口头复述一遍。回来以后,刚才建立的心智模型已经散了一半。
AI 的价值恰好在这里:它可以充当一个临时的“外部工作记忆”,帮我保存问题、证据、假设和下一步。但前提是我不能只扔给它一句“这是什么问题”。
二、我重新设计的闭环:先给证据,再让 AI 动手
我最后稳定下来的流程如下:
这张图里有三个我刻意保留的人工关卡:
- 事实核对。模型可以推理,但它不知道我们项目里的
status=4为什么代表“人工关闭”。 - 变更审查。AI 很容易顺手“优化”用户没有要求改的地方,范围必须由人控制。
- 最终验收。编译通过只是最低门槛,业务数据、权限、并发、回滚都需要真实验证。
为了让每次对话不从零开始,我给任务准备了一个很朴素的上下文模板:
【目标】 新增订单取消原因查询接口,只读,不改现有取消流程。 【验收条件】 1. 仅订单创建人和管理员可查询; 2. 找不到订单返回业务错误 ORDER_NOT_FOUND; 3. 老订单 cancellation_reason 为空时返回“未记录”,不能报错; 4. 必须补 Controller 与 Service 测试。 【已知事实】 - Spring Boot 3.x,JDK 17; - 统一响应体为 CommonResult<T>; - 当前分支已有未提交的前端字典修改,不要触碰; - 权限判断复用 OrderPermissionService。 【相关文件】 - OrderController.java - OrderQueryService.java - OrderPermissionService.java - OrderMapper.xml 【希望你先做什么】 先复述调用链、列出风险和需要我确认的问题,不要修改代码。最后一句很重要。遇到陌生模块时,我不会一上来就让 AI 改代码,而是先让它证明自己理解了问题。它的复述如果错了,后面的代码写得越快,返工越大。
三、第 1 天:不写代码,先给自己的工作做“接口盘点”
第一天我只做了一件事:把最近两周的开发任务分成四类。
| 类型 | 典型任务 | 我给 AI 的权限 |
|---|---|---|
| 信息压缩 | 总结日志、整理调用链、比较配置 | 只读,可大胆使用 |
| 草稿生成 | DTO、测试矩阵、SQL 候选、文档 | 可生成,必须人工审查 |
| 有限执行 | 修改指定文件、运行指定测试 | 明确范围后执行 |
| 高风险操作 | 数据修复、生产配置、权限、删除 | AI 只给方案,不直接执行 |
这个分类看起来简单,却解决了我最初的一个问题:同一个工具,不应该在所有任务上拥有同样权限。
读一段日志和执行一条生产 SQL,风险完全不是一个量级。把“能不能用 AI”问成一个二选一问题没有意义,真正要问的是:
- 它能读哪些上下文?
- 它能改哪些文件?
- 它能运行哪些命令?
- 哪些动作必须再次确认?
- 失败以后能否回滚?
我还建了一个任务日志,每次只记六项:
## 任务:订单取消原因查询 - 开始时间:09:20 - 目标:增加只读查询接口 - AI 参与:调用链归纳、测试矩阵、代码草稿 - 人工决策:权限复用方式、空值兼容策略 - 验证:模块测试 42/42,通过;手工检查 3 类账号 - 复盘:一开始漏掉历史空值,测试矩阵帮助发现如果不记录,人很容易只记住 AI “一把过”的高光时刻,却忘了它制造的返工。任务日志让我能够区分真正节省的时间和只是看起来很快的输出。
四、第 2 天:让 AI 帮我读代码,但禁止它先入为主
维护老项目最累的环节之一,是在陌生模块里找真正的入口。
以前我的做法是全文搜索一个接口名,沿着 Controller、Service、Mapper 一层层点开。现在我仍然这样做,只是先把搜索结果和关键文件交给 AI,让它输出一张“待核对地图”。
我会要求它按固定格式回答:
请只根据我提供的代码回答,不要猜测未出现的实现。 输出: 1. 请求从 Controller 到数据库的调用链; 2. 每一层的输入、输出和副作用; 3. 与权限、事务、缓存相关的代码位置; 4. 你无法确认的地方,统一标记为【待核对】; 5. 最后给出最小阅读顺序,最多 8 个文件。这比“帮我分析一下项目”有效得多。后者通常会得到一段正确但空泛的架构介绍;前者会逼着模型区分证据和推断。
以一个订单状态查询为例,AI 第一次给出的调用链是:
OrderController -> OrderService -> OrderMapper -> t_order看起来没有问题,但项目里实际还有一个切面根据租户重写查询条件,Mapper XML 里又关联了归档表。如果直接按它的第一版理解修改,很可能在测试环境正常、到历史数据查询时失败。
我补充了切面和 XML 后,再让它更新地图。这时 AI 的作用不是“发现一切”,而是把我已经找到的证据组织成可复用的结构。第二天结束时,我最大的感受是:
AI 读代码的上限,取决于它能看到什么;AI 读代码的可信度,取决于它是否被要求标注不知道什么。
五、第 3 天:写接口——从“生成代码”改成“生成最小 Diff”
第三天开始真正改代码。我刻意选择了一个小接口,而不是让 AI 新建一整套模块。
需求是根据订单号查询取消信息。为了让示例聚焦,下面省略项目里的统一异常和权限实现:
publicrecordCancellationView(StringorderNo,Stringreason,LocalDateTimecancelledAt){publicstaticCancellationViewfrom(Orderorder){StringsafeReason=Optional.ofNullable(order.getCancellationReason()).filter(reason->!reason.isBlank()).orElse("未记录");returnnewCancellationView(order.getOrderNo(),safeReason,order.getCancelledAt());}}Controller 没有直接访问 Mapper,而是复用现有查询服务:
@RestController@RequestMapping("/api/orders")@RequiredArgsConstructorclassOrderQueryController{privatefinalOrderQueryServiceorderQueryService;privatefinalOrderPermissionServicepermissionService;@GetMapping("/{orderNo}/cancellation")CommonResult<CancellationView>getCancellation(@PathVariableStringorderNo,@AuthenticationPrincipalLoginUserloginUser){Orderorder=orderQueryService.getRequired(orderNo);permissionService.checkCanRead(loginUser,order);returnCommonResult.success(CancellationView.from(order));}}AI 最初给我的代码更“完整”:它新建了一个 Repository、一个异常类型,还改了统一响应体。单看每一段都说得通,但它把一个小需求扩成了架构改造。
我把提示改成:
只修改我列出的 3 个文件;优先复用现有 Service、异常和响应体; 不要新增依赖,不要重命名公共类型,不要顺手格式化无关代码; 先给出变更计划和预计 diff,再生成实现。第二版明显收敛。
这里有一个很实用的判断标准:让 AI 写“文件”,还是写“变更”?
对全新练习项目,生成完整文件没什么问题;对已有项目,我更关心它相对当前代码改了什么。因此审查时我只看 diff:
- 修改是否超出需求;
- 是否复制了已有能力;
- 是否改变异常语义;
- 是否把敏感信息写入日志;
- 是否引入不必要依赖;
- 是否保留了历史兼容。
当我开始用“最小 diff”约束 AI 后,代码量反而少了,但一次通过率更高。
六、第 4 天:Debug——别把 3000 行日志直接倒给模型
第四天遇到的是一个更真实的问题:测试环境偶发返回 500,本地无法稳定复现。
最初我犯了一个典型错误,把完整日志直接贴进对话。结果 AI 抓住了最显眼的一条连接池警告,给出了一套数据库连接优化建议。但那条警告在正常请求里也存在,真正的异常藏在后面。
我重新整理证据,只保留:
- 第一个业务异常;
- 根因
Caused by前后各 20 行; - 请求 ID 对应的 SQL;
- 发生时间、接口参数和环境差异;
- 一次成功请求的对照信息。
然后让 AI 输出“假设表”,而不是直接给结论:
| 假设 | 支持证据 | 反对证据 | 下一步验证 |
|---|---|---|---|
| 历史订单字段为空 | 失败订单创建时间较早 | 本地新数据无法复现 | 查询该订单原始字段 |
| 权限服务返回空组织 | 日志在权限判断后失败 | 同组织其他订单成功 | 打印组织 ID,不打印用户敏感信息 |
| 缓存旧对象缺字段 | 清缓存后短暂恢复 | 未确认缓存版本 | 对比缓存与数据库对象 |
这个格式会迫使我们把“可能”与“已经证明”分开。
最终根因是缓存中的旧序列化对象没有新字段,反序列化后为null,而下游代码直接调用了trim()。修复不复杂:
privateStringnormalizeReason(Stringreason){if(reason==null||reason.isBlank()){return"未记录";}returnreason.trim();}真正有价值的是复盘:
- AI 第一次判断错,不是因为它完全不懂 Java,而是我给了噪声过多的上下文;
- “请分析根因”太容易得到一个自信结论;
- “列出互斥假设、证据和验证动作”更适合故障排查;
- 没有真实查询和复现,任何根因都只是候选答案。
此后我固定使用下面这段 Debug 提示:
你是排障搭档,不是结论生成器。 请按“现象—证据—假设—验证动作”回答。 至少给出 3 个可能原因,并说明每个原因如何被证伪。 如果证据不足,明确写“当前不能下结论”。 不要建议大规模重构,优先给最小验证步骤。七、第 5 天:补测试——AI 最适合先生成“测试矩阵”
让 AI 直接写单元测试,常见结果是:代码很长,Mock 很全,但只验证了最顺利的路径。
所以第五天我先让它生成测试矩阵:
| 场景 | 输入 | 依赖状态 | 预期 |
|---|---|---|---|
| 正常取消订单 | 合法订单号 | 有取消原因 | 返回原原因 |
| 历史订单 | 合法订单号 | 原因为null | 返回“未记录” |
| 空白原因 | 合法订单号 | 原因为空格 | 返回“未记录” |
| 订单不存在 | 未知订单号 | 查询为空 | 抛ORDER_NOT_FOUND |
| 非订单所有人 | 合法订单号 | 权限拒绝 | 返回无权限 |
| 管理员查询 | 合法订单号 | 管理员权限 | 正常返回 |
确认矩阵后,再让 AI 按项目现有测试风格生成代码。比如对取消原因的参数化测试:
classCancellationViewTest{@ParameterizedTest@NullAndEmptySource@ValueSource(strings={" "," "})voidshouldFallbackWhenReasonIsMissing(Stringreason){Orderorder=newOrder();order.setOrderNo("SO20260725001");order.setCancellationReason(reason);order.setCancelledAt(LocalDateTime.of(2026,7,25,10,30));CancellationViewview=CancellationView.from(order);assertEquals("未记录",view.reason());assertEquals("SO20260725001",view.orderNo());}}针对权限逻辑,我会检查 AI 是否只验证“方法被调用”,还是验证真正的业务结果。下面这种测试就太弱:
verify(permissionService).checkCanRead(any(),any());它只能证明调用发生过,不能证明传入的是正确用户和正确订单。更好的做法是捕获参数,或者直接覆盖允许与拒绝两个结果。
AI 生成测试时还经常出现三类问题:
- Mock 了被测对象内部太多细节,导致重构一下测试就全碎;
- 自己编了不存在的工厂方法和测试基类;
- 为了让测试通过,反过来修改生产代码的可见性。
我的约束是:测试必须先编译;失败时只把第一组关键错误反馈给 AI;禁止为了测试方便扩大生产方法权限。模型能把测试骨架搭得很快,但测试有没有保护真实风险,仍然需要开发者判断。
八、第 6 天:SQL 与文档——让 AI 解释计划,不让它“猜索引”
第六天我把 AI 用在 SQL 优化和接口文档上。
原 SQL 是按租户、状态和创建时间查询订单:
SELECTid,order_no,user_id,status,created_atFROMt_orderWHEREtenant_id=?ANDstatus=?ANDcreated_at>=?ORDERBYcreated_atDESCLIMIT50;如果只把 SQL 发给 AI,它几乎一定会建议建立联合索引:
CREATEINDEXidx_order_tenant_status_createdONt_order(tenant_id,status,created_at);这个建议可能正确,也可能只是“教科书正确”。真实项目还要看:
- 现有索引是否已经覆盖;
status的区分度;- 查询频率与写入成本;
- 实际执行计划是否走索引;
- 是否存在按
user_id的另一类高频查询; - 数据库类型和版本。
所以我提供了脱敏后的表结构、索引列表和EXPLAIN结果,让 AI 逐列解释,再由我在测试库验证。优化前后记录的是扫描行数、回表情况和耗时分布,而不是只看一次查询“快了几毫秒”。
文档部分则更适合 AI。提交前,我会让它根据 diff 生成一版说明:
请根据变更内容生成提交说明,包含: 1. 为什么改; 2. 改了什么; 3. 没改什么; 4. 如何验证; 5. 风险与回滚方式。 不要写“优化了系统性能”这类无法验证的空话。得到的草稿再由我补上真正的业务背景。这样写出来的文档比“新增取消原因接口,详见代码”有用得多,下一位维护者也能知道为什么保留了“未记录”这个兼容逻辑。
九、第 7 天:把零散技巧沉淀成个人 SOP
第七天我没有继续试新工具,而是整理一份每天都能用的清单。
1. 开始任务前
- 用一句话写清目标,不把解决方案当需求;
- 列出验收条件和明确不做的范围;
- 标注相关模块、技术版本和项目约定;
- 区分只读任务、可修改任务和高风险操作;
- 检查上下文里是否有密钥、用户数据、内部地址。
2. 让 AI 分析时
- 先复述问题,再给方案;
- 要求区分“代码事实”“合理推断”“待核对”;
- 限制读取和修改范围;
- 复杂任务先要计划,不要直接要完整代码;
- 故障排查要求假设和证伪步骤。
3. AI 给出修改后
- 只看 diff,不被大段完整代码淹没;
- 检查是否改了无关文件;
- 检查异常、日志、权限和空值;
- 运行最小相关测试,再运行更大范围测试;
- 手工验证至少一个正常场景和一个边界场景。
4. 提交之前
- 清理模型生成的无意义注释;
- 确认没有虚构 API、依赖和配置项;
- 记录验证命令和结果;
- 写清风险与回滚;
- 把本次踩坑补进团队文档或任务记录。
这份 SOP 的意义不是把开发变成流水线,而是把“我脑子里知道要检查的事”外显出来。AI 的输出越快,检查清单越重要。
十、7 天后的时间对比:省下来的不是全部工时,而是低价值耗时
下面是同类任务在一周前后的粗略对比。样本量很小,任务难度也不可能完全相同,所以不要把它当成普遍结论。
| 环节 | 改造前 | 稳定后 | 变化原因 |
|---|---|---|---|
| 需求澄清 | 50 分钟 | 30 分钟 | AI 先生成边界问题清单 |
| 阅读模块 | 90 分钟 | 50 分钟 | 先形成调用链地图,再定点阅读 |
| 接口草稿 | 80 分钟 | 45 分钟 | 复用模板,限制最小 diff |
| Debug | 110 分钟 | 60 分钟 | 压缩日志,使用假设表 |
| 测试设计与编码 | 60 分钟 | 40 分钟 | 先矩阵后代码 |
| 文档与提交说明 | 50 分钟 | 25 分钟 | 根据 diff 生成结构化草稿 |
| 返工与上下文恢复 | 40 分钟 | 20 分钟 | 任务日志保留思路 |
合计从约 8 小时降到约 4.5 小时,但这并不代表以后每天只工作半天。省下的时间很快会被更深入的设计、评审、沟通和新任务填满。对我而言,真正的收益有三点:
- 晚上不再因为机械补文档而拖延;
- 复杂问题的思路不容易在切换窗口时丢失;
- 我能把更多注意力放到业务边界,而不是样板代码。
“8 小时变 4 小时”只有在任务类型合适、上下文清楚、验证手段完整时才可能发生。遇到架构决策、生产事故和复杂历史兼容,AI 甚至可能让前期探索变长。但只要过程留下证据,这种变长也不一定是坏事。
十一、我踩过的 6 个坑
坑 1:提示词写得很长,却没有验收条件
长提示不等于好提示。背景写了两千字,却不说输出要满足什么,模型仍然只能猜。最有效的不是堆角色设定,而是明确目标、范围、约束和验证方式。
坑 2:把整个项目一次性塞进去
上下文越多,噪声也越多。相似的旧实现、过期文档和无关日志会把模型带偏。我的做法是先给入口和失败证据,模型提出缺口后再补文件。
坑 3:看到代码“像能跑”就直接接受
AI 很擅长生成风格正确的代码,也很擅长虚构一个名字非常合理的方法。必须编译、测试、搜索定义,并与项目现有用法对照。
坑 4:让 AI 顺手重构
修 Bug 时顺手改命名、抽公共类、升级依赖,会让 review 范围失控。小任务坚持最小 diff;真正需要重构时,单独立项、单独验证。
坑 5:只计算生成速度,不计算审查成本
模型 30 秒生成 300 行代码,不代表节省时间。如果我花 90 分钟确认它的隐含假设,不如一开始让它生成 30 行最小改动。效率应该按“可交付结果”计算。
坑 6:把敏感数据当普通上下文
日志可能包含手机号、Token、订单号和内部地址;配置文件可能包含密钥。无论使用云端还是本地工具,都应先做数据分级和脱敏。生产写操作、权限变更和数据删除不能因为“AI 建议”就跳过审批。
十二、一套我现在每天使用的提示结构
与其收藏一百条“神级提示词”,不如掌握一个稳定骨架:
角色:你是我的 Java 代码审查搭档。 目标:修复订单取消原因在历史数据下触发的空指针。 上下文: - JDK 17 / Spring Boot; - 已确认数据库允许 cancellation_reason 为 null; - 相关代码和失败测试见下方; - 不允许修改数据库结构。 任务: 1. 先说明根因是否已被证据支持; 2. 给出最小修改方案; 3. 列出需要补的测试; 4. 标记所有不确定项。 约束: - 只修改 CancellationView 和对应测试; - 不新增依赖; - 不改变 API 字段; - 不输出完整项目,只给 diff 说明和必要代码。 验收: - null、空串、空白串均返回“未记录”; - 原有非空原因保持不变; - 相关测试全部通过。这个结构的核心不是“角色”,而是最后三部分:任务、约束、验收。它把模型从自由写作拉回工程协作。
十三、AI 时代,Java 程序员真正要加强什么
一周实验之后,我反而更确定传统工程能力不会失效。
AI 可以很快生成 Controller,却不知道某个接口是否应该公开;可以建议索引,却不知道业务高峰期的写入压力;可以补一堆测试,却不知道哪个失败会让公司真正损失钱。模型越能写,我们越需要判断:
- 需求是否被正确理解;
- 系统边界是否清楚;
- 数据和权限是否安全;
- 代码是否可验证、可回滚;
- 一次修改是否符合长期维护成本。
换句话说,程序员的价值正在从“亲手输入每个字符”转向“定义问题、组织上下文、控制变更和验证结果”。这不是少写代码,而是让代码重新回到解决问题的位置。
总结
我用 7 天完成的不是一次工具迁移,而是一次工作方式重排:
- 把需求写成可验收的任务;
- 把陌生代码整理成可核对的地图;
- 把代码生成限制为最小变更;
- 把 Debug 变成证据和假设的循环;
- 把测试从“补数量”变成“覆盖风险”;
- 把每次交付沉淀成下一次可复用的上下文。
如果你也想尝试,不必同时安装十个工具。明天上班时,挑一个低风险、可验证的小任务:整理一段日志、为一个纯函数补边界测试,或者根据 diff 写提交说明。记录改造前后耗时,也记录返工。
当你能够持续回答“AI 做了什么、我验证了什么、结果为什么可信”,它才真正进入了你的开发工作流。
