在IT编程与信息化项目里,存在一种典型的“灯下黑”:团队看得见代码量、接口报错、部署日志和迭代速度,却常常忽略真正决定系统成败的复杂度。软件工程经典著作《人月神话》和《没有银弹》中,Fred Brooks 将这种复杂度拆成两类:本质复杂度(essential complexity)与偶然复杂度(accidental complexity)。本质复杂度来自问题本身,是业务规则、边界条件、数据一致性等内在难度;偶然复杂度来自我们使用的工具、语言、框架、配置,是技术选型带来的额外负担。AI编程工具,例如 Cursor、GitHub Copilot、通义灵码,确实大幅降低了偶然复杂度,比如生成样板代码、补全接口、编写配置文件。但为什么AI没有代替传统岗位?因为信息化项目里真正消耗时间的并不是代码生成,而是理解需求、拆解业务、权衡架构、保证结果可靠。这跟代码行数和编程速度无关,甚至跟是否使用AI无关。
这篇文章从两类复杂度出发,用一个信息系统中常见的“每日积分结算”需求,逐步拆解 AI 编程能完成什么、不能完成什么,并讨论传统岗位在 AI 时代为什么仍然不可替代。内容适合正在使用 AI 辅助开发的程序员、准备引入 AI 编程工具的团队,以及想理解“AI 替代论”边界的产品和技术负责人。
1. 先区分本质复杂度和偶然复杂度:那段被忽略的“灯下黑”
1.1 用一条积分规则说明复杂度并不在代码表面
一个看起来简单的需求:“每日统计用户积分,并按积分段发放优惠券,重复提交不重复发放。”
这句话里的每个词都需要继续细化。每日是指自然日还是财务日?积分来自订单、评价还是签到?统计的积分快照是什么时间点的?优惠券发放的阈值是累计积分还是当日积分?重复提交的判断依据是同一个用户、同一个批次,还是同一个请求?这些都不是语法问题,而是业务规则和系统边界问题。
在信息化项目中,这类模糊需求非常常见。用户说“看到本月待办事项”,研发需要考虑待办的定义、已办与办结的区别、权限范围、延迟显示、分页和归档。代码量可能不大,但沟通和判断量很大。代码生成之前,大量工作已经决定成败。如果只盯着“能不能用 AI 把接口写出来”,反而会漏掉真正需要花时间的部分。
1.2 本质复杂度:问题域自带的复杂度
本质复杂度是人类要解决问题的内在难度。它不因为换编程语言、换框架、换 AI 工具而消失。
例如财务对账系统,必须保证每一笔分录借贷平衡;库存系统,必须防止超卖;核心交易系统,必须支持事务和幂等;医疗系统,药品配伍禁忌需要完整的领域知识。这些复杂度来自业务本身的约束和不确定性。系统越贴近真实业务,本质复杂度往往越高。
定义上,本质复杂度是问题域内包含的逻辑、约束和不确定性。它无法被删除,只能被理解、拆分、建模和验证。如果需求本身就是复杂的,任何技术手段都不能让复杂度消失,只能把复杂度从一个地方搬到另一个地方。比如把业务规则做进数据库存储过程,规则仍在;把规则做成配置中心,规则仍在;把规则交给 AI 生成代码,AI 只是把规则翻译成代码,规则本身并不会因为 AI 参与而消失。
1.3 偶然复杂度:手段带来的复杂度
偶然复杂度来自实现技术方案的附加成本。它和业务无关,只和“怎么实现”有关。
例如为了跑一个 Python 项目,需要安装依赖、配置虚拟环境、处理 Python 版本与包版本冲突;为了在 Spring Boot 中接入 MySQL,需要配置数据源、写 Mapper、处理连接池参数;为了在 K8s 中发布服务,需要编写 Dockerfile、Service、Deployment 和 ConfigMap。这些工作很有价值,但本质上不是业务规则的组成部分。它们是技术选型、平台限制和工程规范带来的额外工作。
AI 非常擅长处理这一类复杂度。它能根据提示词生成依赖文件、常见配置、CRUD 接口和单元测试骨架,能快速把“不知道这个框架怎么写”变成“差不多能跑”。这也是很多开发者在接触 AI 编程后第一感受是“效率提升明显”的原因。但效率提升主要发生在偶然复杂度层面,而不是本质复杂度层面。
1.4 两种复杂度的核心对比
| 维度 | 本质复杂度 | 偶然复杂度 |
|---|---|---|
| 来源 | 问题域、业务规则、不确定性 | 实现工具、平台、语法、配置 |
| 典型例子 | 权限模型、财务记账规则、并发一致性 | 框架配置、依赖版本、接口样板代码 |
| 能否消除 | 不能消除,只能理解、拆分、建模 | 可以降低,通过封装、工具、AI辅助 |
| 主要承担者 | 需求分析师、架构师、领域专家、资深工程师 | 普通开发者、AI工具、代码生成器 |
| 失败表现 | 上线后业务规则不对、数据不一致 | 编译报错、启动失败、依赖冲突 |
| 关键动作 | 澄清需求、验证边界、设计模型 | 生成代码、查文档、调参数 |
实际项目里,这两类复杂度不是边界清晰的两个盒子。同一个问题可能会同时包含两类复杂度,甚至在项目不同阶段相互转化。但先把这个模型建立起来,再讨论 AI 的能力边界,就会清晰很多。
2. AI编程工具真正擅长消除的是哪一层复杂度
2.1 Cursor、Copilot这类工具的工作方式
AI 编程工具的共同点是:基于大语言模型,根据用户输入的提示词、当前文件上下文和项目结构,生成候选代码。它们擅长补全常见模式、生成重复性代码、转换数据格式、解释某个 API 的用法,以及把自然语言描述翻译成一段看起来合理的实现。
但工具的工作方式决定了它需要足够的上下文。提示词如果只写“生成一个用户注册接口”,没有说明字段、校验规则、密码存储方式、是否支持手机号登录、是否需要图形验证码,AI 输出的只能是通用模板。真正的变化发生在开发者把业务约束输入进去之后。
实际使用中,可以把 AI 编程工具理解成一位非常熟悉语法、但没有业务经验的“结对程序员”。它可以快速写出 FastAPI、Spring Boot、Vue 组件、SQL 查询,但它并不清楚你所在公司对“用户”“订单”“结算”的定义。代码能跑,不代表代码是对的。
2.2 AI对样板代码和语法细节的处理效果
用一个非常小的接口示例说明。提示词可以这样写:
请用 Python FastAPI 编写一个用户积分查询接口。 要求: 1. 输入参数:userId 2. 返回:用户ID、总积分、昨日积分、积分等级 3. 使用 Pydantic 做参数校验 4. 错误时返回统一 JSON 格式AI 生成的代码大致如下:
from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() class UserScoreResponse(BaseModel): user_id: int total_score: int yesterday_score: int level: str @app.get("/users/{user_id}/score", response_model=UserScoreResponse) async def get_user_score(user_id: int): # 此处需要对接数据库与积分计算逻辑 raise HTTPException(status_code=501, detail="not implemented")这段代码结构完整,能帮助开发者快速理解 FastAPI 的接口写法,但它没有实现真正的业务。关键点在于,AI 帮你省掉的是 FastAPI 路由写法、Pydantic 模型定义、异常返回格式这类偶然复杂度。积分等级怎么算、昨日积分从哪里查、用户不存在返回什么,AI 并不知道,只能留出占位。
这正是 AI 编程工具在真实项目中的常态:它能生成模板,但你必须知道模板里应该填什么业务。
2.3 异步编程和 CompletableFuture 中的AI局限
热门话题里经常出现“异步编程”和“CompletableFuture 异步编程异常处理”。用异步批量处理任务举例,AI 可以快速生成一个教科书写法:
ExecutorService executor = Executors.newFixedThreadPool(10); for (Task task : tasks) { executor.submit(() -> process(task)); }这段代码在小型学习项目中可以跑通,但放到生产环境会带来一串问题:线程池队列有没有上限?任务执行失败后是否记录日志?应用重启后未完成的任务怎么办?多个应用实例同时执行同一个任务会不会重复处理?任务超时如何中断?
AI 不会主动问你“corePoolSize 设置为多少合适,任务失败要不要重试,重试会不会影响上游系统”。它只会根据常见模式继续生成。对 AI 来说,异步编程的语法是确定的,但异步系统的异常处理、幂等控制、资源隔离和取消机制是业务层面的取舍。这些取舍必须由工程师判断,而不是由代码生成器决定。
2.4 从复杂度模型看 AI 的能力边界
| 操作类型 | AI 当前角色 | 人类决策责任 |
|---|---|---|
| 代码补全 | 生成候选代码 | 判断是否满足业务语义 |
| 样板配置 | 生成模板 | 判断环境与版本匹配 |
| 单元测试 | 生成基础用例 | 覆盖需求边界与异常场景 |
| 架构设计 | 只给出通用方案 | 结合团队、规模、成本做取舍 |
| 需求澄清 | 无法替代访谈与确认 | 必须由人主导 |
| 故障定位 | 辅助分析日志 | 理解全链路与业务影响 |
可以得出结论:AI 解决的是“已经知道要做什么,但不知道高效写出来”的偶然复杂度;它对“不知道要做什么”的本质复杂度没有判断能力。接下来进入信息化项目的真实场景,看看这个边界如何体现。
3. 在信息化项目中,为什么AI没有替代传统岗位
3.1 信息化系统的UE设计原则背后是业务沟通
现代信息化项目强调用户体验设计,但 UE(User Experience)不只是在界面上调整按钮位置。设计一个审批系统的“待办列表”,需要判断审批角色、权限范围、会签还是或签、超时是否自动通过、意见是否必须填写、列表如何排序。这些规则来自组织流程,而不是来自设计规范。
AI 可以帮助生成前端的列表组件、状态标签、分页交互,但无法替代产品经理和业务方逐条确认“超时自动通过”是否符合管理制度。信息化系统的 UE 设计原则,本质上是对业务逻辑的表达方式。一个用户看不到的字段,可能决定了整个流程是否合规。这也是为什么“AI 生成页面”只能作为原型,不能直接成为生产系统的原因。
3.2 软件开发流程里的“编码”只占一段
一个信息化项目的生命周期通常包括:需求调研、范围确认、方案设计、资源预算、开发、测试、部署、培训、运维。代码编写在其中只是其中一段,远不是项目全貌。更常见的是,大量时间花在核对需求文档、评审接口设计、准备环境、处理联调问题、迁移数据、培训用户、响应线上故障。
AI Agent 可以自动提交代码、生成发布说明、整理周报,但无法替代项目负责人去协调多个部门的数据口径,无法替代测试人员去设计边界用例,也无法替代运维人员判断回滚条件。传统岗位不只是写代码,还承担流程推进、责任归属和多方沟通。AI 可以把“编码”这个环节进一步压缩,但压缩不了业务确认和系统稳定性的时间。
3.3 传统岗位的本质不是写代码,而是做决策
当需求出现冲突时,比如财务部门要求数据实时准确,运营部门希望批量导入后立即可用,AI 无法判断哪个部门优先级更高。当技术方案出现分歧时,比如用单张宽表还是多张业务表,AI 无法知道团队维护能力和未来扩展方向。这个决策过程依赖领域知识、权衡意识和风险判断。
程序员写的每一行代码都在固化业务规则。规则一旦理解错误,后续所有自动化工具都会放大错误。AI 可以快速生成“看起来正确”的代码,但“正确”的标准来自业务约束和验收标准。传统岗位的价值,恰恰体现在定义这些约束上:能问出“结算日期按自然日还是财务日”的工程师,比能写一万行代码但从不追问细节的人更难被替代。
3.4 信息化项目服务的真实场景:判断、责任与回滚
生产环境出现故障时,AI 可以辅助分析日志,但“是否回滚”“先恢复服务还是先保留现场”“通知哪些业务方”是需要人来拍板的。上线前,也要有人确认配置文件、权限账号、备份策略。这个责任边界很难由代码生成工具承接。
AI 可以解释某个报错的可能原因,比如“连接池满可能是因为连接未释放”,但它无法承担系统故障的责任,也无法在事故复盘时向业务方解释为什么做出这个决策。信息化项目最终影响的是真实业务流程,一旦出错,需要有人负责,也需要有人给出恢复方案。这种责任机制决定了传统岗位不会因为代码生成能力提升而消失。
3.5 岗位职责与AI能力对照表
| 岗位角色 | 日常职责 | AI 可协助 | AI 无法替代 |
|---|---|---|---|
| 需求分析师 | 澄清需求、输出规则 | 整理文档、生成模板 | 与业务方访谈和确认 |
| 架构师 | 技术选型、拆分模块 | 对比方案、生成架构草图 | 结合团队和成本做决策 |
| 开发工程师 | 编码、自测、代码审查 | 生成代码、补测试 | 判断业务正确性、修复深层问题 |
| 测试工程师 | 设计用例、发现缺陷 | 生成基础用例 | 判断用户真实使用场景 |
| 运维工程师 | 部署、监控、故障应急 | 生成巡检脚本、解读日志 | 掌握回滚与应急决策 |
| 项目经理 | 排期、协调、风险管理 | 生成周报、整理会议纪要 | 跨团队沟通与资源协调 |
与传统印象不同,AI 不是把岗位变少,而是把岗位中最模板化、最可描述的部分提速。剩余部分更多是判断、协商和承担责任,这部分恰恰是传统岗位竞争力的核心。
4. 用一个最小业务案例验证AI编程的边界
4.1 需求描述:每日积分结算
案例背景是一个电商系统的“用户积分结算”功能。需求是:
- 每天凌晨计算前一天的用户积分。
- 按积分区间发放优惠券。
- 一个人一天只结算一次,不能重复发放。
- 支持失败后重跑,但重跑不能产生重复数据。
这个需求很适合用来验证 AI 编程边界,因为它需要业务规则、定时任务、并发控制和幂等控制。它不像“生成一个用户注册接口”那样可以直接抄模板,而是需要先定义结算口径和处理边界。
4.2 人类工程师要关心的关键点
写代码前,工程师要确认至少五个问题:
- 结算口径:基于前一天的订单还是当前积分快照?退货订单是否参与?
- 积分来源:下单积分、评价积分、签到积分是否都计算在内?
- 重复处理:同一批数据被重复执行时,如何保证不重复发放?
- 并发场景:定时任务多实例部署时,会不会同时执行同一个用户?
- 失败补偿:处理到一半任务失败,如何重跑且不产生重复数据?
为了支撑这些规则,需要一张结算记录表:
CREATE TABLE user_settlement ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, settle_date DATE NOT NULL, total_score INT NOT NULL, coupon_id BIGINT, status TINYINT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_date (user_id, settle_date) );唯一键uk_user_date是幂等控制的基础。不管定时任务被调度多少次,同一天同一个用户只能插入一条记录。核心处理逻辑可以这样理解:
public void settle(String settleDate) { // 1. 检查是否已经存在结算批次,避免重复执行 // 2. 查询当日需结算的用户列表 // 3. 对每个用户,先尝试插入 user_settlement // 4. insert 成功才计算积分并发券 // 5. update 状态为成功 // 6. 捕获唯一键冲突,跳过已结算用户 }这里还需要明确事务边界:计算积分和发券不能放在同一个长事务里,否则大批量用户时会造成锁竞争;插入记录与后续发券必须独立,发券失败要能重试。这些都不是语法问题,而是对业务一致性的理解。
4.3 AI生成的“伪完整”代码
如果直接让 AI“写一个每日积分结算的 Java 方法”,生成结果往往长这样:
public void settleDailyScore() { List<User> users = userRepository.findAll(); for (User user : users) { int score = scoreService.calculate(user); Settlement settlement = new Settlement(); settlement.setUserId(user.getId()); settlement.setScore(score); settlementRepository.save(settlement); if (score >= 1000) { couponService.issue(user.getId(), "COUPON_1000"); } } }这段代码确实完成了“遍历用户、计算积分、保存记录、发券”的流程,但缺少:
- 查询范围:没有按日期过滤,会把所有用户全部结算一遍。
- 幂等控制:没有检查唯一键,重复执行会再次插入记录。
- 异常处理:发券失败不会影响后续用户,也可能导致状态不一致。
- 批量处理:百万用户全部加载到内存,可能导致内存溢出。
- 事务边界:一个用户失败可能导致全部回滚或部分成功。
如果把它直接部署到生产环境,第二天会出现大量重复发券和数据错误。AI 在答案形式上完整,但在约束理解上缺失。它生成的是“从常见代码中拼接出来的样子”,而不是“当前系统真正需要的实现”。
4.4 验证与排查链路
按“输入、处理、输出、异常”的顺序检查这个功能:
- 输入:结算日期是否正确传入,跨天时有没有时区问题。
- 处理:同一用户重复执行时,插入是否被唯一键拦截。
- 输出:结算记录中的积分与订单明细金额是否一致。
- 异常:发券接口超时后,结算状态是否还是成功;重试后是否发两次。
排查时查看数据库记录:
SELECT user_id, settle_date, status, coupon_id FROM user_settlement WHERE settle_date = '2025-01-01' ORDER BY user_id;如果coupon_id为空但status为成功,说明发券步骤没有补偿;如果同一个user_id出现两条记录,说明唯一键没有生效。
同时也可以检查定时任务日志:
# 检查定时任务调度日志 grep "settle" /app/logs/schedule.log # 查看数据库是否有锁等待 show processlist;生产环境还需要增加分布式锁,例如使用 Redis 锁或数据库锁表,避免多个应用实例同时执行同一个任务。这些额外的保障,AI 不会主动提醒,需要由熟悉系统整体架构的工程师补上。
5. AI编程时代的常见误区和排错思路
5.1 误区一:AI能生成代码就说明程序员不重要
现象:团队在接入 Cursor 后,要求研发快速堆功能,同时把代码评审和单元测试减到最少。结果,AI 生成的代码在简单场景没有问题,一旦遇到数据一致性、权限校验、安全防护,就出现线上故障。
原因:AI 生成的代码是“平均概率”下的模式,不是针对当前系统的确定性实现。它没有经过业务验证,也没有经过测试用例覆盖。
排错方式:保留代码评审、单元测试、联调验证。把 AI 生成的代码当成“新同事提交的初稿”,谁提交谁负责。
5.2 误区二:提示词写得越多,需求就越清晰
现象:很多人以为把提示词写得很长,AI 就能理解业务。实际上,提示词再长,也只能描述表象,不能替代需求确认。
正确做法:先写用户故事和验收标准,再让 AI 生成代码。验收示例如下:
Given 用户 2025-01-01 已结算 When 再次执行结算任务 Then 系统不重复发放优惠券把验收标准交给 AI,输出会更接近需求。如果直接让 AI“补全功能”,它只会生成看起来合理但缺少约束的代码。
5.3 误区三:让AI自动修复bug等于拥有一条安全链路
现象:把报错日志贴给 AI,AI 给出修改建议,研发直接复制。但报错有时只是表象,真正原因在更上层。例如“数据库连接池满”可能来自慢查询,而不是连接池配置。
处理顺序:先确认输入数据,再确认文件路径和权限,再看依赖版本,再看配置是否生效,最后看业务逻辑。AI 是辅助排错工具,必须由人来判断“这个因果关系是否成立”。不能让 AI 的修改建议跳过人工验证。
5.4 使用AI代码的排查顺序
AI 生成的代码如果出现问题,建议按以下顺序排查:
- 确认需求输入:参数、状态、日期是否正确。
- 检查数据库约束:唯一键、外键、索引是否按设计创建。
- 检查事务边界:是否有全量回滚或部分提交。
- 检查并发控制:多实例是否同时执行,幂等键是否生效。
- 检查异常处理:是否吞掉异常,重试是否造成重复副作用。
- 检查日志:是否打印了足够上下文。
5.5 常见问题速查表
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| AI 生成的代码无法编译 | 缺少依赖或版本不匹配 | 查看 pom.xml / requirements.txt | 核对依赖版本,替换为项目一致版本 |
| 功能能跑但结果不对 | AI 未理解业务规则 | 对照需求文档和验收标准 | 补全业务规则后再生成或修改 |
| 数据重复执行 | 缺少幂等控制 | 查看表结构和任务日志 | 增加唯一键或分布式锁 |
| 发券失败但状态成功 | 异常处理不完整 | 查看发券记录与结算状态 | 增加补偿任务和失败重试 |
| 定时任务未执行 | 调度配置错误或时区问题 | 检查调度平台日志 | 统一使用服务器时区和测试任务 |
6. 传统岗位如何借助AI继续保持竞争力
6.1 用AI处理偶然复杂度,把时间留给本质复杂度
建议团队建立“AI 辅助但不替代”的工作流。AI 负责生成项目脚手架、单元测试骨架、常用 SQL、文档初稿;开发人员负责补充业务规则、边界条件、安全校验和异常处理。这样能把时间从“背 API、查框架语法”转向“理解业务、设计模型”。
学习环境可以快速用 AI 生成 demo,测试各种想法。生产环境一定要人工审查和测试。一个可落地的策略是:在提交代码前增加 AI 生成的代码质量抽查,检查是否有硬编码、是否缺幂等、是否缺少输入校验。
6.2 程序员应该重点训练的判断力与抽象能力
AI 越强,人类越需要训练 AI 不擅长的能力:
- 需求拆解:把一个模糊目标拆成可验证的功能点。
- 领域建模:把业务规则映射成数据结构、状态机和表关系。
- 一致性设计:理解事务、幂等、并发、补偿。
- 取舍判断:知道什么时候用简单方案,什么时候引入复杂框架。
- 沟通确认:能问出“结算日期按自然日还是财务日”这类关键问题。
这些能力需要在实际项目中反复验证,不是看几集教程就能获得。AI 生成代码的速度越快,对这些能力的检验就越明显:一个能澄清需求的人,会让 AI 产出可直接使用的代码;一个只会堆需求的人,只会得到一堆需要返工的空壳。
6.3 可复用的AI辅助开发检查清单
在把 AI 生成的代码合入主分支前,可以逐项检查:
- 是否明确输入、输出和错误返回。
- 是否补齐了权限、校验和敏感字段处理。
- 是否包含幂等和重复执行保护。
- 是否定义异常处理路径,而不是吞异常。
- 是否考虑了批量数据量级和性能。
- 是否包含单元测试或本地调试验证。
- 是否与现有数据库表结构和命名规范一致。
- 是否经过代码评审。
将这个清单作为团队 PR 模板,可以显著减少 AI 代码引入的生产问题。它不是一个摆设,而是把“人工判断”落到流程里的具体方式。
6.4 扩展方向:从AI辅助编码走向AI Agent与领域建模
下一步不是继续在“让 AI 写更多代码”上竞争,而是把 AI Agent 用于自动化测试、代码审查、日志分析和知识库检索。例如使用 Spring AI 接入企业内部文档,构建“项目规则问答机器人”,让新人在提问时先获得业务规则说明。再例如使用 AI Agent 自动生成定时任务巡检报告,把异常数据提取出来给人工确认。
这些建设的前提,都是人先定义好业务边界和数据模型。本质复杂度不会消失,但 AI 可以让人把更多精力放到它上面,减少偶然复杂度带来的疲劳。这才是传统岗位与 AI 工具共存的方向。
回到文章开头说的“灯下黑”。程序员最容易忽视的不是代码怎么写,而是代码背后的复杂度在哪里。AI 把代码生成变得更容易,反而把人的判断力推到更关键的位置。真正不可替代的,是对业务规则的理解、对数据一致性的敬畏、对系统故障的责任感。与其担心 AI 取代岗位,不如把 AI 当作一面镜子:它让偶然复杂度不再占用你的时间,也同时提醒你,本质复杂度永远需要人面对。