AI 编程时代,为什么脚手架依然不可替代?
AI 能写代码了,为什么我还是建议你用脚手架?
“AI 给我生成了一个用户注册接口,上线第一天就被 SQL 注入了。”
这是一位真实开发者在技术社群里的吐槽。
一、AI 编程的"暗面":那些没人告诉你的坑
2025-2026 年,AI 编程工具全面爆发。Cursor、Claude Code、GitHub Copilot、通义灵码、WindSurf……你随便拉一个开发者,他都在用 AI 写代码。
但冷静下来看看真实情况:
1.1 AI 生成的代码,安全漏洞遍地
AI 训练数据来自开源社区,而开源社区里有大量不安全的代码。AI 会毫不犹豫地"学习"这些坏习惯:
// AI 可能给你生成这样的代码(真实案例)@GetMapping("/user/{name}")publicUsergetUser(@PathVariableStringname){Stringsql="SELECT * FROM user WHERE name = '"+name+"'";returnjdbcTemplate.queryForObject(sql,newUserRowMapper());}// ↑ SQL 注入,一行搞定// AI 可能这样处理密码user.setPassword(request.getPassword());// 明文存储,nice// AI 可能这样处理权限@DeleteMapping("/admin/user/{id}")publicvoiddeleteUser(@PathVariableLongid){userService.deleteById(id);// 没有权限校验,任何人调接口就能删用户}你让 AI 写 100 个接口,可能 30 个有安全问题。而你一个都看不出来——因为 AI 生成的代码"看起来很专业"。
1.2 AI 不懂你的架构,每次都在"重新发明轮子"
假设你的项目已经有一套异常处理体系:
// 你们项目的标准写法@PostMapping("/order")publicResult<OrderVO>createOrder(@RequestBody@ValidOrderCreateDTOdto){returnResult.success(orderService.create(dto));}// 异常由 GlobalExceptionHandler 统一捕获// 业务异常抛 BizException,框架自动包装成标准响应但 AI 不知道。它会给你生成这样的代码:
// AI 生成的写法——完全无视你项目的规范@PostMapping("/order")publicMap<String,Object>createOrder(@RequestBodyMap<String,Object>params){Map<String,Object>result=newHashMap<>();try{// 业务逻辑混在 Controller 里Orderorder=newOrder();order.setProductName((String)params.get("productName"));order.setPrice(newBigDecimal((String)params.get("price")));orderMapper.insert(order);result.put("code",200);result.put("msg","success");result.put("data",order);}catch(Exceptione){result.put("code",500);result.put("msg",e.getMessage());// 异常信息直接暴露给前端}returnresult;}每一个新对话,AI 都在从零开始"设计"你的系统。它不知道你项目有统一的Result封装、有统一的异常处理、有 DTO 校验规范。它只知道「你要一个创建订单的接口」。
1.3 AI 生成的代码无法维护
这是最致命的问题。想象一下:
第一天:让 AI 写了用户模块 → Result<T> 返回格式 第二天:让 AI 写了订单模块 → Map<String, Object> 返回格式 第三天:让 AI 写了支付模块 → ResponseEntity 返回格式 第四天:让 AI 写了商品模块 → 自定义 ResponseVO 返回格式同一个项目,4 种返回格式。后面接手的人(可能是三个月后的你自己)看着这堆代码,只能默默关掉 IDE。
更别提:
- 有的接口用
@Valid校验参数,有的手动 if-else - 有的模块用 MyBatis-Plus,有的直接写 JdbcTemplate
- 有的地方用了缓存,有的地方查询没走缓存
- 有的接口有权限校验,有的完全没有
- 异常处理方式五花八门:有的 try-catch,有的抛异常,有的返回 null
1.4 多租户?数据权限?AI 根本想不到
如果你做的是 SaaS 系统,需要多租户隔离:
// AI 完全不会考虑租户隔离@GetMapping("/orders")publicList<Order></