AI 编程工具进入日常开发后,很多团队最关心的是“AI 能不能把活干完”,或者“生成的代码能不能通过测试”。但有一个问题被低估了:当程序员越来越依赖 AI 补全、生成、修复代码时,编程专业技能是否在一步步退化。这里的退化不是指失业,而是指当环境切换到没有 AI 的场合,或者遇到 AI 无法理解复杂业务约束时,人的判断力、调试能力、代码审查能力和系统设计能力明显下降。
这篇文章不打算讨论“AI 会不会取代程序员”。更值得讨论的是:依赖 AI 正在如何改变程序员的认知方式,专业技能崩溃到底会不会发生,以及在日常工作里,是否有一套可执行的训练方法,能让 AI 辅助和技能成长同时存在。文章会从能力构成、认知机制、自查方法、工程实践和训练排期几个角度展开,尽量给出可以直接落地的判断标准。
1. 编程专业技能并不等于“会写语法”,它是一套分层能力
1.1 专业技能的分层:语法、问题解决、系统判断
很多新手误以为编程技能是“记住语法和 API”。实际上,能写代码只是最表层的能力。专业的编程技能可以拆成三层:
| 能力层 | 主要内容 | 常见表现 | AI 工具能替代吗 |
|---|---|---|---|
| 表层:语法与工具 | 语言关键字、常用库、框架 API、快捷键 | 能写出可运行的函数 | 能,而且替代程度最高 |
| 中层:问题解决 | 算法选择、数据结构运用、边界条件、异常处理、调试思路 | 能定位 bug、能改对逻辑 | 部分能,但需要人判断需求是否完整 |
| 深层:系统判断 | 架构决策、模块拆分、权限模型、并发与一致性权衡、性能优化 | 能设计系统、能评审他人代码 | 很难,因为依赖上下文和经验 |
AI 最容易替代的是第一层。也正因为它能轻松替代表层能力,很多开发者的职业训练慢慢被压缩成了“输入提示词 + 粘贴代码 + 修一下编译错误”。时间一长,中层的调试能力和深层的系统判断能力会因为没有足够练习而退化。
1.2 技能崩溃不是一夜发生的,而是练习机会消失的结果
技能提高依赖有效练习。有效练习通常包含三个环节:尝试、失败、修正。程序员需要先自己写出有缺陷的方案,然后从异常、测试失败或评审意见里得到反馈,再修正心智模型。
当 AI 直接给出正确代码时,失败反馈被跳过了。人跳过了一次“因为不了解边界条件所以犯了错”的机会。一次两次影响不大,但每天都这样,大脑就会失去对错误信号的敏感度。等到系统里出现一个 AI 也答不出来的复杂并发问题时,调试经验几乎为零的人会完全不知道从哪下手。
所以严格说,AI 不会直接把谁的技能打崩。它是让练习机会消失,技能是慢慢“饿死”的。
2. 为什么“看得懂结果”不等于“掌握了能力”
2.1 被动接收代码,记忆留存率很低
心理学里有“生成效应”这个现象:通过主动回忆、自己重构出来的内容,比被动阅读记得更牢。写代码就是一种典型的生成式行为。大脑在组织逻辑、选择 API、处理错误分支时,会建立更牢固的记忆痕迹。
使用 AI 编程时,最常见的动作是:读一眼生成的代码,觉得逻辑对,直接回车或点击接受。这个动作实际上等同于“快速扫读一篇文章”。扫读时,大脑认为这些内容已经出现在屏幕上,不需要费力编码,因此记忆会很快消失。这也是为什么很多开发者发现,自己昨天让 AI 写的代码,今天完全不知道某一段是干什么的。
2.2 AI 提供的正确结果,掩盖了错误的推理过程
对初学者来说,看到 AI 直接给出正确答案,反而更危险。写错的体验对构建直觉是有价值的。比如一个日期处理函数,新手容易忽略时区,写了new Date()直接格式化。如果 AI 直接给出带时区处理的完整代码,新手看到的是“正确答案”,但依然不理解为什么时区会出问题。
下次遇到类似的海外用户时间显示问题,他还是不会判断数据源头、数据库时区和前端时区的关系。正确结果掩盖了推理过程中的盲区,这是 AI 编程最隐蔽的副作用。
2.3 一个能运行但不一定正确的例子
下面这段代码看起来很简单,也完全能“运行”:
def parse_csv_line(line: str) -> list[str]: return line.split(",")如果让 AI 生成 CSV 解析函数,在没有约束的情况下,它大概率会给出类似答案。但这个函数在生产环境会出问题:字段里如果包含逗号,比如"Hello, world",123,直接split(",")会把它拆成三段;字段包含换行符时,结果更不可控。要修好它,必须理解带引号字段的转义规则:
import csv from io import StringIO def parse_csv_line(line: str) -> list[str]: reader = csv.reader(StringIO(line)) return next(row for row in reader)这个前后对比说明一个关键问题:AI 生成的简单版本不一定错,但它缺少对业务边界的理解。如果开发者全盘接受 AI 结果,很多人连“这里应该用 csv 模块”的意识都不会产生。技能是否牢固,不在于会不会用split,而在于知道split会在什么场景下失效。
3. 依赖 AI 后,最典型的四类技能退化表现
3.1 调试路径断裂:报错时不知道先看哪里
依赖 AI 修复问题的开发者,遇到报错的第一反应往往是复制错误日志到对话窗口。这本身不是问题,问题是长期下来,人不再主动构建“日志 -> 代码 -> 数据流向 -> 根因”的推理链。
看一个实际错误日志:
Exception in thread "main" java.lang.NullPointerException at com.example.OrderService.calcTotal(OrderService.java:47) at com.example.OrderController.submit(OrderController.java:21)不借助 AI,合理的调试顺序应该是:
- 先定位
OrderService.java第 47 行,看是哪个对象为空。 - 往上追踪这个对象是从哪个参数、哪个方法传入的。
- 判断是调用方没传值,还是内部方法返回了
null。 - 再决定修复点:加空值校验、改调用方传参,还是修正上游数据。
如果一个人习惯了让 AI 逐步提示,这些基础判断链会慢慢模糊。技能退化的第一个信号就是:拿到错误日志后大脑空白,只能靠工具推动自己思考。
3.2 代码审查空转:只检查风格,不检查语义
AI 生成的代码通常格式规范、命名清晰,审查起来“表面很舒服”。但真正危险的 bug 几乎都在语义层,而不是语法层。
| 语义问题类型 | AI 生成时常见情况 | 人工审查常犯的错误 |
|---|---|---|
| 空值处理不完整 | 在正常路径没暴露,遇到空数据才出现 | 只确认主流程能跑 |
| 并发写冲突 | 直接写共享变量,不考虑锁或原子操作 | 忽略多线程测试覆盖 |
| 时区处理错误 | 使用应用服务器默认时区 | 只验证本地环境 |
| 浮点精度丢失 | 金额字段使用float或double | 没看存储类型 |
| 资源未关闭 | 连接、文件流、游标打开未释放 | 没做长时间运行测试 |
| 边界条件缺失 | 循环、分页、数组越界没有覆盖 | 只测正常数据 |
如果审查者每天都在逐行确认语法,却没意识到“这里的double存金额会失真”,那么代码库里会积累大量“能运行但语义错误”的模块。这种错误会让系统在特定用户、特定数据量、特定地区爆发故障,而且很难排查。
3.3 设计能力弱化:需求被转译成“给 AI 的几句话”
正常的需求实现路径是:需求分析 -> 约束识别 -> 模块拆分 -> 接口设计 -> 编码实现。依赖 AI 对话之后,这个过程可能被压缩成:把需求描述得稍微清楚一点 -> 复制生成代码 -> 组装进项目。
问题在于,AI 看不到真实业务场景。它不知道导出订单时要考虑当前租户的数据权限,不知道同一用户短时间内重复点击会造成重复支付,也不知道历史文件不能覆盖。真正的设计能力是识别这些隐藏约束,而不是把需求文本转成一个提示词。
当开发者长期不负责约束识别,只负责把 AI 输出粘进工程时,他实际上是在用“提示词工程”代替“需求建模”。这类技能一旦缺失,遇到没有历史资料、没有成熟方案的项目,会非常被动。
3.4 工具锁定:离开特定 AI 环境就无从下手
依赖还有一个很容易被忽视的表现:开发者的能力被绑定在某款工具里。比如用 Cursor 或 GitHub Copilot 时,会自动补全上下文、自动生成重复代码;一旦切换到纯文本编辑器或没有插件的生产服务器,很多人连项目结构骨架、配置文件格式、依赖注入方式都记不全。
真正的专业技能应当具有环境迁移能力。框架会换、语言会换、AI 工具也会换,但“理解模块边界”“会看崩溃栈”“能设计数据表”这些能力是可迁移的。如果一个人的工作流完全依赖工具的记忆,而不是自己的心智模型,那么换一次工具就等于能力清零一次。
4. 在工程实践中建立“AI 辅助但不替代”的工作流
4.1 先写设计和伪代码,再让 AI 补实现
防止技能退化的第一个原则是:人必须先输出设计,AI 再补充实现。不要让 AI 从零号需求开始生成模块,而应该先定义边界和约束。
以一个订单导出功能为例。让 AI 直接写代码前,先自己列出约束点:
# 订单导出 CSV 的设计约束 # 1. 只能导出当前租户有权限的订单,不允许跨租户查询。 # 2. 单次导出超过 5000 条时,进入异步任务,避免阻塞主请求。 # 3. 文件名必须包含业务日期,避免同一天多次导出互相覆盖。 # 4. 金额做四舍五入保留两位小数,并输出为字符串,避免浮点展示问题。 # 5. 导出失败时记录任务日志,不影响用户主流程。然后,把这份设计作为提示词的输入,要求 AI 严格执行。实现完成后,人需要逐条核对自己的设计约束是否被满足。这个流程里,核心设计权仍然在人这边,AI 只是执行者。
4.2 对 AI 产出做“先测试后信任”的审查
审查 AI 代码的标准不应该停留在“能不能运行”,而应该是“能不能通过我写的测试”。
推荐的最小闭环是:先写测试,再让 AI 实现。比如计算订单折扣:
def test_discount_is_zero_under_one_hundred(): assert calculate_discount(99) == 0 def test_discount_is_ten_percent_above_one_hundred(): assert calculate_discount(100) == 10 def test_discount_rounds_to_two_decimals(): assert calculate_discount(99.99) == 0 assert calculate_discount(101.01) == pytest.approx(10.1, abs=0.01)测试用例本身就是人工设计的,它反映了对业务规则的理解。AI 只负责实现,而人工负责定义什么是正确。这个过程既保证了代码质量,也让“写测试”这项核心技能得到持续训练。
4.3 把 AI 产出纳入代码评审,而不是直接合入
团队里应有明确的约定:AI 生成代码必须标注来源,评审人不能只做流程性审批,要针对语义问题提问。一个好的评审提问包括:
- 这段生成代码覆盖了哪些异常分支?
- 如果上游数据为空,这段代码会怎样?
- 这段代码在高并发下是否安全?
- 后续同事维护时,能否不借助 AI 说明就理解它的意图?
如果每个人都在用 AI 生成代码,但没有人在审查时研究这些问题,代码库最终会变成“每个函数看起来都合理,但组合起来谁都不敢动”的状态。维护这样的系统,比没有 AI 时更难。
4.4 区分生成型任务与判断型任务
实践中,可以把任务分成两个类型:
| 任务类型 | 举例 | 建议交给 AI 吗 | 原因 |
|---|---|---|---|
| 样板代码生成 | DTO、Mapper、基础 CRUD、实体类 | 可以 | 规则明确,出错容易发现 |
| 结构化数据转换 | JSON 转对象、字段映射 | 可以,但需测试 | 遗漏字段难以察觉 |
| 基础算法实现 | 排序、遍历、常用工具函数 | 可以,但需补边界测试 | 边界条件是风险点 |
| 异常处理策略 | 何时重试、何时抛错、何时降级 | 不建议 | 依赖业务上下文 |
| 权限模型设计 | 角色、数据范围、操作权限 | 不建议 | 安全相关,判断责任重大 |
| 数据库表结构 | 索引选择、事务边界 | 不建议 | 影响数据一致性 |
| 架构拆分 | 模块划分、服务边界 | 不建议 | 直接决定系统演进成本 |
一个人可以安全地用 AI 处理大量生成型任务,把精力留在判断型任务上。真正的专业能力,恰恰体现在判断型任务里。
5. 防止技能崩溃的日常训练清单
5.1 每周安排固定“无 AI 编码时段”
给正在练习的开发者一个建议:每周抽出 1 到 2 小时,关闭所有 AI 编程工具,手工完成一个小任务。任务难度不要太高,比如:
- 在不查文档的前提下,写出一个链表反转。
- 手工实现一个带过期时间的本地缓存。
- 不借助自动补全,写出一个带分页查询的 SQL。
- 从零配置一个新的 Spring Boot 或 Python 项目骨架。
做完后检查三点:哪些 API 记错了、哪里花时间最多、哪些逻辑自己根本不理解。这三个答案就是下次学习的重点。
5.2 每月重写一个由 AI 生成的模块
每个月选择一个已经由 AI 完成的功能模块,不看 AI 的提示,不复制原代码,手工重写一遍。重写完后,对比两个版本的差异:
| 对比项 | 原 AI 版本 | 手工重写版本 |
|---|---|---|
| 完成耗时 | 例如 20 分钟 | 例如 1 小时 |
| 函数数量 | 5 | 6 |
| 异常处理 | 关键路径异常已处理 | 增加了空值和部分失败处理 |
| 对代码的理解 | 能运行但不完全理解 | 能解释每一行为什么存在 |
| 测试覆盖 | 无 | 补充了两个边界测试 |
如果重写版本在结构上明显优于 AI 版本,说明技能正在恢复。如果重写版本和 AI 版本几乎一样,说明自己只是记住了答案,不是理解了逻辑。
5.3 用“讲给他人听”来验证理解程度
一个非常有效的检验方法是费曼式解释:找一段最近写的代码,用自然语言讲清楚每一步为什么存在。讲不出来的地方,就是理解漏洞所在。
5.4 团队层面的机制建议
团队管理层面,可以建立三条规则。
第一条,提交信息里标注“AI 生成”与“人工编写”,方便追溯和复盘。第二条,新人的学习任务不允许直接依赖 AI,至少要手工完成前三个模块,再绑定 AI 工具。第三条,定期组织故障复盘,重点不是修复过程,而是每个问题的推理链路:日志里哪个字段给了提示、为什么先怀疑这个模块、最后怎么定位根因。
这些措施看起来严格,但能有效抵抗“全员进入复制粘贴模式”的团队技能滑坡。
6. 判断力依然是最核心的编程专业技能
6.1 应该拥抱 AI 的场景
AI 编程工具确实提高了效率。在以下场景中,放心使用它并不会造成技能崩溃:
- 生成重复的样板代码。
- 把旧代码翻译成新语言或新框架。
- 生成测试数据。
- 辅助快速搜索 API 用法。
- 在已经理解需求边界的前提下,用 AI 加速编写实现。
在这些场景里,人的判断力仍然在掌控结果。AI 是加速器,不是方向盘。
6.2 应该在哪些时刻收起 AI
以下时刻,建议先关闭 AI 工具,自己推理:
- 学习一个新框架或新语言时,不要一上来就生成示例代码。
- 线上故障排查时,不要一开始就把日志丢给 AI,先自己做链路分析。
- 设计数据表、权限模型、支付流程时,不要直接让 AI 给方案,先列出约束条件。
- 评审别人代码时,不要用 AI 的“代码解释插件”代替自己的理解。
这些场景的共同特点是:一旦依赖 AI,就会跳过构建心理模型的关键过程。而心理模型,才是程序员真正的专业技能。
6.3 最后的核心判断
AI 编程工具不会自动摧毁编程技能,真正危险的是使用方式。回到文章标题:对人工智能的依赖确实可能导致编程专业技能崩溃,但这个“依赖”是有边界的。如果把所有代码生成、bug 修复、架构设计都外包给 AI,人的判断、调试、设计能力会失去练习对象,技能崩溃只是时间问题。如果让 AI 做“执行层”的工作,把“判断层”留在自己手里,它反而能挤出更多时间用来训练更深层的能力。
程序员这个职业真正值钱的地方,不是会调用 API,而是在混乱的业务中定义边界、在下游数据出错时判断故障、在没有历史方案时做出取舍。这些能力不会因为 AI 出现而失去价值,但会因为过度依赖 AI 而逐渐萎缩。要防止技能崩溃,不需要拒绝工具,只需要保留每周几次无辅助实践,并且永远把 AI 的输出当作待验证的假设,而不是最终答案。