尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

AI时代手写代码并未过时:手动编程的不可替代价值与工程实践

AI时代手写代码并未过时:手动编程的不可替代价值与工程实践 这段时间和不少后端同学聊过同一个话题AI 编程工具越来越强是不是意味着“手写代码”这件事正在贬值甚至有人直接说以后程序员只需要会写 prompt 就行手写代码要过时了。我自己的感受恰恰相反。确实在生成样板代码、补全函数、写 CRUD 接口这些场景里AI 的效率高得惊人我在实际项目中也经常用。但真正进入复杂业务、性能调优、系统设计、线上故障排查的时候核心能力依然是“能不能把问题看清、拆透、写稳”。AI 可以帮你把想法变成代码但前提是你得先有想法——这个想法质量决定最终代码质量。而“想法”的源头就是手动编程经验带来的理解力与判断力。这篇文章不打算贩卖焦虑也不打算否定 AI。我想以一个普通后端开发者的视角聊聊手动编程在 AI 时代为什么没有过时它的不可替代价值到底在哪里以及我个人在项目里总结出的“AI 辅助 手动掌控”实际用法。全文会用真实可运行的代码示例和项目场景来展开适合正在使用 AI 编程工具、同时对自身技术成长有要求的开发者阅读。1. 背景与核心概念AI 时代手写代码地位真的动摇了吗1.1 “手写代码过时”的说法从哪里来从 GitHub Copilot 大面积普及到 ChatGPT、Claude、Cursor 等工具进入日常开发流程现在的 AI 编程工具确实能根据一段自然语言描述生成可运行的函数、接口、甚至完整模块。很多团队里出现了一种现象需求评审后开发者先把需求粘贴给 AI让它生成初版代码然后复制到 IDE 里改改报错调试通过后提交Code Review 时大家对着 AI 生成的代码面面相觑因为没人能完全解释每一行的意图。这种流程跑顺之后自然会出现“手写代码过时论”。因为从表面看从“想法”到“代码”的距离被无限缩短了人的角色似乎只剩下去描述需求和修报错。但从工程视角看这里有一个致命的盲区AI 生成代码 ≠ 你理解代码。代码上线之后维护它、扩展它、排查它的人仍然是程序员。如果没有人能说清楚某段逻辑为什么这样写出了问题就只能靠试错和猜这是工程上最危险的状态。1.2 手动编程到底是什么这里必须先界定概念否则讨论容易跑偏。本文说的“手动编程”不是指拒绝使用任何工具、坚持用记事本敲每一行代码。而是指程序员能够在不依赖 AI 的情况下把需求拆解为清晰的数据结构和算法流程程序员理解代码的运行机制包括内存、IO、并发、异常等底层行为程序员有能力独立写出健壮、可维护、边界处理完整的代码程序员能够审查、修改、重构 AI 生成的代码而不是只会粘贴和抱怨。换句话说手动编程代表的是编码能力本身而不是“手写”这个动作。AI 时代的程序员仍然需要这种能力甚至更重要。1.3 为什么这个话题值得每个开发者认真看待现在一线互联网公司招聘后端工程师几乎无一例外考察手写算法、手写 SQL、手写并发编程题并且要求解释原理。为什么因为面试官很清楚工具可以辅助生成代码但无法替代候选人的工程判断力。在实际工作中这种判断力体现在太多场景里一个接口响应慢是数据库索引问题、N1 查询问题还是锁竞争问题一个分布式事务方案应该用 TCC 还是本地消息表一个线上偶发超时是 GC 停顿、网络抖动还是连接池不够这些问题没有标准化的 prompt 能直接解决。你必须依赖对系统原理和代码细节的深刻理解而这些理解只能通过亲手编写、调试、维护代码来积累。AI 能给你代码但不能给你经验。2. AI 辅助编程的能力边界它能做什么不能做什么2.1 AI 编程工具擅长的事情先说清楚AI 编程工具的价值是真实存在的。我在日常开发中经常会用 AI 处理下面这类任务生成 Java Bean、DTO、VO 等样板代码编写 Spring Boot 项目的 Controller/Service/Mapper 基础结构生成正则表达式、日期格式化、Base64 编解码等工具方法为已有方法补充单元测试模板把一段复杂的 SQL 改写成更可读的版本解释陌生框架源码中的某个方法调用链。这些任务的特点是逻辑相对固定、边界清晰、上下文依赖少。AI 在这种场景下确实能把人从重复劳动中解放出来让我们把精力放在更重要的设计上。下面用一个简单例子说明。假设我需要一个工具方法把 List 集合按固定大小分批// 文件路径src/main/java/com/example/common/ListPartitionUtil.java public class ListPartitionUtil { public static T ListListT partition(ListT source, int batchSize) { if (source null || batchSize 0) { throw new IllegalArgumentException(source must not be null and batchSize must be positive); } ListListT result new ArrayList(); for (int i 0; i source.size(); i batchSize) { int end Math.min(i batchSize, source.size()); result.add(new ArrayList(source.subList(i, end))); } return result; } }这种代码让 AI 生成几秒钟就能完成质量也很稳定。用它来节省时间完全合理。2.2 AI 编程工具做不好的事情但如果把问题升级一下AI 的短板就暴露了。比如需求是给定一个英文文本统计单词出现次数按照“次数降序次数相同按字典升序”返回前 K 个词。这个需求看起来很简单但里面有一个典型的边界陷阱当两个单词出现次数相同时必须按字典顺序排序而不仅仅是按次数排序。如果 prompt 里没有强调这一点AI 很可能会生成下面这种代码// 文件路径src/main/java/com/example/demo/TopKFrequentWords.java public class TopKFrequentWords { public ListString topKFrequent(String[] words, int k) { MapString, Integer countMap new HashMap(); for (String word : words) { countMap.put(word, countMap.getOrDefault(word, 0) 1); } ListString candidates new ArrayList(countMap.keySet()); // 只按次数排序没有处理次数相同的情况 candidates.sort((a, b) - countMap.get(b) - countMap.get(a)); return candidates.subList(0, k); } }这段代码在“相同次数”这个场景下就会出错。比如输入是[a, b, a, b, c]a和b都出现两次按字典序应该返回[a, b]但上面的代码返回的可能是[b, a]因为排序规则没有定义次关键字。这种问题叫做“隐含需求”。AI 只能根据你明确表达的内容生成代码无法自动补全你没有说清楚的业务规则。而识别这些隐含规则恰恰是手动编程经验的核心价值。如果你没有能力判断 AI 的输出是否完整这类 bug 就会无声无息地进入线上。2.3 从能力边界看手动编程的回归通过上面的对比我们能得出一个清晰的结论AI 擅长把“明确、局部、规范”的想法转化为代码AI 不擅长处理“模糊、全局、未定义明确规则”的任务真正的工程难点从来不是“写代码”而是“定义要写什么、怎么写才算正确”。所以 “AI 时代还要不要手动编程” 其实是个伪命题。正确的问题是在 AI 的辅助下程序员如何把更多精力用来提升定义问题和设计方案的能力。而这两种能力都离不开手动编程的长期训练。3. 手动编程的不可替代价值理解、设计、判断力3.1 价值一只有手写过才能真正理解代码项目中经常遇到一个现象AI 生成了一段代码线上出 bug 了开发者盯着代码看了很久却找不到原因。不是他不够聪明而是他对代码背后机制的理解不够。举个例子。AI 生成了一个遍历集合并删除元素的代码// 错误写法示例遍历过程中删除元素 ListString list new ArrayList(Arrays.asList(a, b, c, d)); for (String item : list) { if (b.equals(item)) { list.remove(item); } }这段代码在运行时大概率抛出ConcurrentModificationException。原因在于 for-each 循环背后使用的是Iterator迭代器会维护一个modCount校验值。当集合在迭代过程中被结构性修改例如 remove时modCount发生变化迭代器在下一次next()时就会检测到并发修改并抛异常。如果程序员只接触 AI 生成的代码遇到这个异常时第一反应是“把 AI 换一种方式写”而不是理解Iterator的fail-fast机制那这个问题就会反复出现。但如果手写过Iterator、读过ArrayList源码就会立刻明白问题根源并给出正确写法// 正确写法示例使用 Iterator.remove() ListString list new ArrayList(Arrays.asList(a, b, c, d)); IteratorString iterator list.iterator(); while (iterator.hasNext()) { String item iterator.next(); if (b.equals(item)) { iterator.remove(); } }这就是理解的价值。AI 可以在几秒钟内给你代码但无法替你在脑海中建立“这段代码为什么这样写、运行时会经过哪些路径、异常会在什么条件下爆发”的模型。没有这种模型代码就是一堆随机堆积的符号。3.2 价值二只有手动编写才能做好架构设计写一个简单的 CRUD 接口AI 可以胜任。但如果要设计一个订单系统、一个支付回调链路、一个多租户数据隔离方案AI 就无法独立完成。原因是这类任务的核心不是“某一段代码怎么写”而是“模块怎么划分、边界在哪里、数据如何流转、失败如何恢复”。我参与过的一个订单系统就是典型例子。最初的需求很简单用户下单后调用库存服务扣减库存调用优惠券服务核销优惠券最后生成订单记录。但如果直接让 AI 写一个createOrder方法AI 会把所有逻辑堆到一个方法里导致库存服务和优惠券服务耦合在订单服务里任何一个下游调用失败整个订单创建就回滚用户无法重试缺少消息队列削峰双十一场景直接打爆数据库。正确的做法是用手动设计思路拆解模块用户下单请求 ↓ 订单服务负责校验、编排 ├─ 库存服务预占库存支持超时释放 ├─ 优惠券服务锁定优惠券支持取消 ├─ 订单数据库创建订单状态机待支付/已支付/已取消 ↓ 返回订单号 异步任务 ├─ 超时未支付 → 自动释放库存、解锁优惠券 ├─ 支付成功 → 发送消息到 MQ → 通知仓储发货这种拆分是 AI 无法替你做的因为它需要理解业务语义、失败恢复策略、团队协作边界。而且模块边界拆分完之后具体某个方法的实现反而可以再交给 AI。也就是说AI 是很好的执行者但不是合格的架构师。3.3 价值三手动编写才能守住质量与安全底线这是我最看重的一点。AI 生成代码时默认目标是“让代码能跑”而不是“让代码在极端情况下安全可靠”。在安全性要求高的场景里这个差异极其致命。举一个典型的例子。AI 很容易生成下面这种拼接 SQL 的代码// 高危写法示例SQL 注入风险 public ListUser findUserByName(String name) { String sql SELECT * FROM user WHERE name name ; return jdbcTemplate.query(sql, userRowMapper); }如果name来自前端且没有校验攻击者传入 OR 11就能查出所有用户。手写代码时这种注入风险是基础安全意识的一部分我们会下意识使用PreparedStatement占位符// 安全写法示例参数化查询 public ListUser findUserByName(String name) { String sql SELECT * FROM user WHERE name ?; return jdbcTemplate.query(sql, new Object[]{name}, userRowMapper); }类似的场景还包括文件上传接口没有做路径穿越校验导致攻击者写入任意目录权限校验注解漏加导致越权访问异常捕获范围过宽吞掉关键告警密钥直接硬编码进配置文件并被推到仓库。AI 生成的代码通常不会主动考虑这些安全边界。如果你不具备手动审查和加固代码的能力把 AI 输出直接上线等于把安全隐患一起带进生产环境。在金融、医疗、政务等强监管行业这类问题更是零容忍。所以安全底线仍然要靠程序员手动把关。4. 实战对比同一个需求两种工作流差距有多大4.1 业务场景描述为了更直观地说明手动编程的价值我们用一个实际的开发任务来对比两种工作流。任务如下有一个 Nginx 访问日志文件access.log每行格式为IP - - [时间] 请求 状态 响应大小。请统计文件中每个 IP 的访问次数按访问次数降序输出前 10 个 IP。注意日志文件可能很大几个 GB不能一次性读入内存。这是一个非常典型的日志分析小工具也是后端开发很容易遇到的需求。下面分别演示“完全依赖 AI 的流程”和“手动设计 AI 辅助的流程”。4.2 工作流 A直接把需求丢给 AI很多人的做法是把需求直接粘贴给 AI拿到代码后复制进项目跑一下。下面是一段常见输出// 文件路径src/main/java/com/example/logtop/IPCounter.java import java.io.*; import java.nio.file.*; import java.util.*; import java.util.stream.*; public class IPCounter { public static void main(String[] args) throws IOException { // 读入所有行 ListString lines Files.readAllLines(Paths.get(access.log)); MapString, Long countMap lines.stream() .map(line - line.split( )[0]) .collect(Collectors.groupingBy(ip - ip, Collectors.counting())); ListMap.EntryString, Long list new ArrayList(countMap.entrySet()); list.sort((e1, e2) - Long.compare(e2.getValue(), e1.getValue())); for (int i 0; i 10 i list.size(); i) { System.out.println(list.get(i).getKey() list.get(i).getValue()); } } }这段代码看起来能用但如果真的处理几个 GB 的日志会出现几个严重问题Files.readAllLines会把整个文件一次性读入内存大文件直接 OOMsplit( )[0]对日志格式的假设太脆弱IP 前面可能有空格、时间格式里有空格、请求行里也有空格无法保证索引 0 就是 IP没有处理空行、格式异常的日志行程序可能直接抛 ArrayIndexOutOfBoundsException排序时把所有 IP 全量排序而只需要 Top 10效率偏低。这些问题的共同点是什么它们都源于“没有对需求做工程分析”。AI 只是把描述翻译成了代码它不知道“几个 GB”意味着什么也不知道日志格式有多少种变体。4.3 工作流 B手动拆解需求 AI 辅助实现换一种工作流。拿到需求后先手动拆解关键约束日志文件可能很大必须流式读取逐行处理只需要输出 Top 10可以用固定容量的小顶堆避免全量排序日志格式需要容错不能因一行格式异常导致整体崩溃提取 IP 的规则要明确通常是每行第一个字段但要跳过空行。设计好方案后再把“流式读取文件并按正则解析 IP”这段具体实现交给 AI这样 AI 生成的是局部工具代码而不是整个解决方案。下面是我在项目中采用的完整实现// 文件路径src/main/java/com/example/logtop/IPCounter.java import java.io.*; import java.util.*; import java.util.regex.*; public class IPCounter { private static final Pattern IP_PATTERN Pattern.compile( ^((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d) ); public static void main(String[] args) throws IOException { String filePath args.length 0 ? args[0] : access.log; int topN 10; MapString, Long countMap new HashMap(); try (BufferedReader reader new BufferedReader(new FileReader(filePath))) { String line; while ((line reader.readLine()) ! null) { String ip extractIp(line); if (ip ! null) { countMap.put(ip, countMap.getOrDefault(ip, 0L) 1L); } } } // 小顶堆保存 Top N PriorityQueueMap.EntryString, Long heap new PriorityQueue( Comparator.comparingLong(Map.Entry::getValue) ); for (Map.EntryString, Long entry : countMap.entrySet()) { heap.offer(entry); if (heap.size() topN) { heap.poll(); } } // 从堆中弹出并逆序输出 LinkedListMap.EntryString, Long result new LinkedList(); while (!heap.isEmpty()) { result.addFirst(heap.poll()); } for (Map.EntryString, Long entry : result) { System.out.println(entry.getKey() entry.getValue()); } } private static String extractIp(String line) { if (line null || line.trim().isEmpty()) { return null; } Matcher matcher IP_PATTERN.matcher(line.trim()); if (matcher.find()) { return matcher.group(); } return null; } }代码说明BufferedReader逐行读取内存占用只和“单行长度 计数 Map 大小”相关和大文件总量无关用正则从行首提取 IP遇到异常行直接跳过不影响整体统计使用容量固定为 10 的小顶堆避免全量排序在 IP 基数很大时节省大量时间主流程清晰读取 → 计数 → 堆排序 → 输出。这个方案的每一步都是程序员手动设计的结果AI 只承担了局部代码生成和语法补全。即使哪天不用 AI这套思路也能完全手写出来。4.4 两种工作流的对比结论维度工作流 A全权交给 AI工作流 B手动设计 AI 辅助大文件处理容易 OOM流式读取内存可控格式容错脆弱一行异常即崩溃跳过异常行持续统计排序效率全量排序小顶堆 Top N 优化可维护性能跑但不知所为何改结构清晰便于扩展风险意识弱强差距不是“代码行数”而是工程完整度。AI 工具缩短的是“想法→代码”的时间但“想法”是否完善仍然取决于手动设计和编码经验。5. 常见困惑与应对思路AI 时代编程的典型问题5.1 “AI 写的代码还要改半天值得用吗”很多人遇到的情况是AI 生成的代码问题很多改的时间比手写还长于是干脆放弃 AI。其实这是使用姿势错了。正确做法是不要用 AI 去生成完整业务模块而是用它生成局部、边界清晰的部分。例如让 AI 写一个工具类、生成一个正则表达式、把 JSON 转成 Java 对象这些任务上下文明确AI 输出质量高值得用。而涉及业务规则和模块协作的逻辑先自己设计好文档和接口再让 AI 填充实现。5.2 “AI 写的代码看不懂是不是我落伍了”看不懂 AI 生成的代码通常不是因为 AI 太先进而是因为基础不牢。比如 AI 生成了一段使用Stream的复杂管道代码如果读不懂Collectors.toMap的冲突处理说明需要补 Java 集合和 Lambda 基础。这时候的正确动作不是去背 AI 的输出而是拿着这段代码去查资料、读源码、手动改写一遍。亲手改过的代码才会内化成能力。5.3 “手写代码效率太低会不会被会用 AI 的人淘汰”这方面需要明确一个竞争维度淘汰你的不是 AI而是“会用 AI 的资深工程师”。资深工程师使用 AI 时能给出精确的描述、能判断输出是否符合工程规范、能在 AI 犯错时快速修正。这些能力全部来自手动编程经验。所以“用手写代码提升自己”和“用 AI 提升效率”并不冲突。合理路径是新人阶段多用 AI 加速学习但必须手动重写核心代码确保理解熟练后把 AI 当作结对编程伙伴各司其职。5.4 “面试时能不能用 AI 写代码”正式的技术面试几乎不允许使用 AI 工具。原因在于面试要考察的是你大脑里的模型而不是你调用工具的能力。面试官通常会追问为什么用HashMap而不是TreeMap这个循环的时间复杂度是多少如果并发量翻十倍你的方案会怎么改如果平时过于依赖 AI这些问题就会成为致命短板。建议在面试准备阶段完全关闭 AI 工具用手写方式刷题、做系统设计、写核心组件。这个过程看起来很“笨”但恰恰是最扎实的成长方式。5.5 “公司不让用 AI 工具是不是跟不上时代”很多企业出于代码安全、合规审计和数据隐私考虑限制员工使用外部 AI 编程工具。这种限制背后是合理的安全意识。外部 AI 工具会把代码片段发送到第三方服务对于涉及用户隐私、商业机密的项目风险确实很高。在这种环境里手动编程能力就是核心竞争力。同时可以通过公司内部的私有化大模型或合规白名单工具来获取 AI 辅助能力但核心开发和审查仍然依赖个人。6. 最佳实践与工程建议AI 时代程序员如何自处6.1 黄金法则你只接受能通过 Code Review 的 AI 代码不管 AI 生成了什么最终提交前必须经过人工审查。审查标准包括你能逐行解释它做了什么边界条件和异常路径有明确处理没有引入不必要的依赖或重复代码代码风格与团队规范一致涉及安全操作时已确认无注入、越权、敏感信息泄露。如果有一项不满足就不要提交。这条法则能避免 AI 代码成为团队的技术债黑洞。6.2 三层掌控原则业务层、技术层、运行层用 AI 编程时建议始终保持三个层面的掌控业务层你知道这段逻辑要解决什么业务问题为什么这样设计技术层你知道它用了哪些技术组件、数据结构和算法能解释复杂度和并发行为运行层你能预判它在生产环境的表现包括异常、性能、日志、监控指标。任何一层失去掌控代码都不应该进入生产。这也意味着手动编程训练不能停。每学到一种新框架、新语法都应该手动写一个最小示例理解它的行为再决定是否引入到项目里。6.3 用 AI 生成测试用例但由人来补全边界写单元测试是 AI 的强项它很快能生成覆盖正常路径的用例。但测试的价值恰恰在于覆盖边界和异常路径。下面是我常用的做法让 AI 根据需求生成基础测试用例手动补充分支条件测试空输入、null、超大值、并发冲突、超时等检查断言是否合理不仅仅是“代码跑通”而是“断言真正反映了需求”对修复过的 bug先手动编写一个能复现的测试用例再让代码通过测试。这样既利用 AI 提升效率又保住了质量底线。6.4 安全底线高风险代码必须人工主导以下类型代码建议不要把最终决定权交给 AISQL 变更、数据库索引设计、事务边界定义权限校验、认证授权逻辑加密密钥、令牌生成、支付回调验签分布式锁、消息队列消费幂等任何涉及删除、批量更新、生产环境变更的脚本。这类代码一旦出错损失不可控。正确流程是人工完成方案设计AI 可以提供参考片段但引入生产前必须经过严格测试和双重审查。如果使用自动化脚本在生产环境执行变更务必先在测试环境完整验证并保留备份和回滚方案。6.5 维护手写能力的 3 个日常习惯具体到每天的工作节奏我建议保持三个习惯第一每周选一块核心代码完全手写。可以是一个分布式锁工具、一个限流算法、一个数据结构封装。手写的目的不是为了生产使用而是保持对细节的敏感度。第二每次让 AI 生成代码后主动追问自己这个实现的时间复杂度是多少异常路径覆盖全了吗如果是线上场景它还成立吗这种追问是驱动能力增长的关键。第三定期做代码走查。不要只看自己写的代码也要主动 review AI 生成或同事提交的代码。带着挑错的心态去读能快速暴露自己在异常处理、并发控制、安全防护等方面的盲区。7. 总结与学习路线从“会用 AI”到“掌控 AI”写到这里核心观点已经很清晰了AI 时代手动编程没有过时它从一个“产出技能”变成了“判断技能”。以前手写代码的价值在于产出可运行的代码现在手动编程的价值在于让你能理解、审查、设计、守护代码。如果你刚入行最重要的不是追求用 AI 写出更复杂的应用而是打好基础数据结构与算法、计算机网络、操作系统、数据库原理、设计模式。这些知识很难靠 prompt 获得必须通过手动编码和反复调试来沉淀。如果你已经工作几年建议把 AI 工具当作“结对编程的初级工程师”让它提供候选实现你来做决策和审查。你会发现当你的手动编程能力越强AI 的产出质量就越高因为你更擅长向它提出精确的约束、更擅长识别它的错误、更擅长把它的片段整合进大系统中。AI 是工具人才是目的。保持手写代码的能力意味着你永远拥有理解系统、改造系统和在关键时刻修复系统的主动权。这种主动权在 AI 时代不仅没有贬值反而因为越来越稀缺而变得更加珍贵。
返回列表