AI编程时代,资深工程师如何用规则编译经验

AI编程时代,资深工程师如何用规则编译经验

1. 为什么“写了10年代码的人”在AI编程时代不是被淘汰,而是突然成了稀缺资源?

我带过三届校招新人,也和十多个不同行业的技术负责人聊过团队现状。去年底有个特别典型的场景:一家做工业视觉检测的公司,CTO拉着我看了他们新上线的AI辅助开发流程——前端用Cursor写Vue组件,后端用Claude Code生成Go微服务接口,连单元测试都自动生成了。他挺高兴,说“效率翻倍”。但聊到第三天,他压低声音问我:“老张,你帮我看看这个PR——它把所有异常都吞掉了,还把数据库连接池大小硬编码成1024,上线三天崩了两次。AI写的代码,我居然得花比以前多三倍时间去审,这算哪门子提效?”

这就是标题里那句“反而最值钱”的真实切口:AI不是在替代经验,而是在放大经验的价值杠杆。它把“写代码”这个动作的边际成本压到了近乎为零,但同时,把“判断什么该写、什么不该写、为什么这么写、哪里会出问题”这些隐性知识的权重,推到了前所未有的高度。

你写10年代码,真正沉淀下来的从来不是语法或API调用——那是搜索引擎30秒就能解决的事。你真正拥有的,是那些没写进文档里的“条件反射”:

  • 看到一个SELECT * FROM users WHERE email = ?的SQL,立刻意识到它可能触发全表扫描,因为email字段没建索引;
  • 看到一个HTTP请求超时设为5秒,马上联想到下游依赖的P99延迟是4.8秒,这个配置等于在悬崖边跳舞;
  • 看到一个函数名叫processData(),第一反应是“它到底处理什么数据?清洗?聚合?还是加密?”,而不是直接跳进函数体看实现。

这些能力,AI现在根本学不会。它没有生产环境的“痛感”,没经历过凌晨三点被PagerDuty叫醒排查内存泄漏,也没在Code Review里被前辈一句“这里用Redis Pipeline能省掉80%的网络RTT”点醒过。它只能基于文本模式匹配和概率预测来“猜”,而你的经验,是唯一能把AI的“猜”锚定在现实世界物理约束上的坐标系。

所以,“让AI知道你有什么”,本质不是教AI背诵你的项目经历,而是把你脑子里那些模糊的、直觉的、甚至自己都说不清的“判断依据”,转化成AI能识别、能解析、能执行的结构化信号。就像给一台高精度显微镜装上校准标尺——没有标尺,它再清晰也看不到细胞核的真实位置;没有你的经验信号,AI再快也写不出真正健壮的代码。

这解释了为什么热搜词里反复出现.cursorrulesclaude.mdconfig.yaml这些文件名。它们不是配置文件,而是你个人经验的“数字孪生接口”。当别人还在用自然语言和AI对话时,你已经把十年踩过的坑、总结的范式、团队约定的红线,编译成了AI可读的规则集。这才是“值钱”的底层逻辑:你卖的不是劳动力,而是把混沌经验翻译成机器语言的能力。

提示:别急着去网上搜“claude.md模板”。模板是死的,你的项目才是活的。一个电商系统的claude.md,和一个嵌入式STM32项目的claude.md,核心约束完全不同——前者关心并发一致性,后者关心中断响应时间。照搬模板,等于把别人的校准标尺装在自己的显微镜上,结果只会更糟。

2..cursorrulesclaude.md:不是配置文件,而是你的“经验编译器”

很多人看到热搜里反复出现.cursorrulesclaude.md,第一反应是“赶紧下载个通用模板填进去”。这恰恰掉进了最大的认知陷阱。这些文件真正的价值,不在于它们“是什么”,而在于它们“怎么被编译出来”。

我拆解过二十多个真实团队的claude.md文件,发现一个惊人规律:写得越“规范”的模板,实际使用率越低;而手写、散乱、甚至带注释吐槽的版本,反而在团队里高频迭代、持续生效。为什么?因为前者是“知识搬运”,后者是“经验蒸馏”。

举个具体例子。某支付中台团队的claude.md开头是这样写的:

# 支付核心服务开发规范(Claude Code专用) ## 数据库操作 - 所有SQL必须使用参数化查询,禁止字符串拼接 - 查询单条记录必须使用`SELECT id, name, status FROM ...`,禁止`SELECT *` - 写操作必须包裹在事务中,且事务内只包含必要语句 ## 异常处理 - 不得捕获`Exception`或`Throwable`,必须精确到业务异常类型 - 日志必须包含trace_id和用户id,格式:`[trace:xxx][user:123] payment failed: timeout`

看起来很专业,对吧?但它在实际使用中几乎失效。为什么?因为Claude Code看到“禁止字符串拼接”,它确实会避免"SELECT * FROM users WHERE id = " + id,但它完全可能生成String sql = "SELECT * FROM users WHERE id = ?"; jdbcTemplate.query(sql, id)——这依然违反了“禁止SELECT *”的本意,而AI根本意识不到。

真正起作用的,是他们后来重写的版本,核心段落是这样的:

# 【关键约束】支付服务数据库访问(Claude Code必须强制遵守) ## 规则ID: DB-SELECT-FIELD-EXPLICIT - **触发条件**:当生成SQL查询语句时,且目标表为`payment_order`, `user_account`, `merchant_info` - **必须行为**:显式列出所有需要的字段,字段列表必须来自以下白名单: - `payment_order`: id, order_no, amount, status, created_at, updated_at - `user_account`: id, user_id, balance, frozen_balance, version - `merchant_info`: id, merchant_id, name, settlement_cycle - **禁止行为**:生成包含`SELECT *`、`SELECT %`、`SELECT ALL`等通配符的语句 - **错误示例**:`SELECT * FROM payment_order WHERE order_no = ?` → 违反DB-SELECT-FIELD-EXPLICIT - **正确示例**:`SELECT id, order_no, amount, status FROM payment_order WHERE order_no = ?` ## 规则ID: DB-TRANSACTION-MINIMAL - **触发条件**:当生成涉及`INSERT`/`UPDATE`/`DELETE`的SQL时 - **必须行为**:将该SQL包裹在`@Transactional`注解的方法内,且该方法内不得包含HTTP调用、文件IO、非数据库操作 - **错误示例**:在`@Transactional`方法内调用`httpClient.post(...)` → 违反DB-TRANSACTION-MINIMAL

看到区别了吗?前者是给人看的“原则”,后者是给AI吃的“指令”。它把模糊的“禁止”转化成了可检测的触发条件+必须行为+禁止行为+正反例四元组。Claude Code在生成代码前,会先扫描当前上下文(比如正在编辑的Java类、调用的DAO层方法名、当前文件路径),匹配到payment_order表的操作,就自动激活DB-SELECT-FIELD-EXPLICIT规则,强行约束字段列表。

这就是“经验编译器”的本质:你不是在写文档,而是在写一套微型DSL(领域特定语言),把你的直觉判断翻译成机器可执行的if-else逻辑。.cursorrules同理,它更偏向于Cursor IDE层面的行为控制,比如:

{ "rules": [ { "id": "no-console-log-in-prod", "description": "禁止在生产环境代码中使用console.log", "appliesTo": ["*.js", "*.ts"], "when": { "fileContains": ["console.log(", "console.warn("], "filePathMatches": ["src/", "app/"] }, "then": { "block": true, "suggestion": "请改用logger.info()或logger.warn(),并确保日志级别配置正确" } } ] }

这个规则不是靠AI“理解”什么是生产环境,而是靠硬编码的filePathMatchesfileContains字符串匹配。它笨,但绝对可靠。

注意:config.yamlclaude.md的分工非常明确——config.yaml管“怎么运行”,比如设置模型、超时、代理;claude.md管“写什么内容”,是你的知识输入源。试图在config.yaml里定义业务规则,就像试图用汽车说明书教司机怎么漂移,方向就错了。

3.repo-scan:让AI真正“读懂”你的项目,而不是只看当前文件

很多开发者抱怨:“AI总在瞎猜!我明明在写订单服务,它却给我生成一堆用户管理的代码。” 这不是AI的问题,是你没给它提供足够的上下文“锚点”。repo-scan这个概念,就是解决这个问题的终极方案——它让AI从“单文件编辑器”升级为“全仓库导航员”。

我见过最典型的失败案例,是一个物联网平台团队。他们用Cursor开发设备接入网关,AI总是把MQTT消息解析逻辑写成HTTP风格的RESTful路由,比如@PostMapping("/device/message")。团队反复在claude.md里强调“本项目使用Netty+MQTT,无HTTP Controller”,但毫无效果。直到他们启用了repo-scan,并在根目录放了一个project-context.md

# 项目核心架构(供AI全局理解) ## 通信协议栈 - 设备侧:MQTT 3.1.1 over TLS 1.2 - 网关侧:Netty 4.1.x 实现MQTT Broker - 服务侧:通过本地Unix Socket与Netty进程通信,**无任何HTTP端口暴露** ## 关键模块映射 | 模块名 | 目录路径 | 核心职责 | 常见误用 | |---------|-----------|------------|-------------| | mqtt-broker | /gateway/netty-mqtt | 处理MQTT CONNECT/PUBLISH/ACK | ❌ 误加Spring Web依赖 | | device-service | /service/device | 设备状态同步、指令下发 | ✅ 依赖mqtt-broker模块 | | auth-service | /service/auth | JWT签发、设备证书管理 | ✅ 通过gRPC调用device-service | ## 技术红线(AI生成时必须检查) - 禁止出现`@RestController`, `@RequestMapping`, `@GetMapping`等Spring MVC注解 - 禁止出现`HttpClient`, `RestTemplate`, `WebClient`等HTTP客户端类 - 所有设备指令必须通过`DeviceCommandSender.send(command)`发送

启用repo-scan后,AI在生成任何代码前,会先扫描整个仓库,提取project-context.md中的结构化信息,构建一个轻量级的知识图谱。当它看到你在/gateway/netty-mqtt/src/main/java/com/example/mqtt/handler/下编辑文件时,会自动关联到“通信协议栈”中“MQTT over TLS”的约束,并过滤掉所有HTTP相关的代码片段。

更关键的是,repo-scan能自动识别你的项目“指纹”。比如,它扫描到pom.xml里有<artifactId>netty-all</artifactId><version>4.1.97.Final</version>,再结合/gateway/netty-mqtt/目录存在,就会推断出“这是一个基于Netty 4.1的MQTT服务”,而不是泛泛地认为“这是个Java项目”。这种基于真实代码结构的推理,远比你手动在claude.md里写一百遍“用Netty”要精准。

实操中,repo-scan的威力体现在三个层次:

  1. 文件级上下文:AI知道你当前编辑的MqttMessageHandler.java属于netty-mqtt模块,会优先参考该模块下的README.mdDesignDoc.md
  2. 仓库级约束:AI读取根目录的project-context.md,明白整个系统禁用HTTP,从而在生成DeviceCommandSender的调用示例时,绝不会出现restTemplate.postForObject()
  3. 跨模块依赖:AI扫描到device-service模块的pom.xml里声明了对netty-mqtt<scope>compile</scope>依赖,就知道DeviceCommandSender的实现必然在netty-mqtt包内,生成调用代码时会自动补全正确的import路径。

这彻底改变了人机协作的范式:过去你是“指挥官”,每一步都要告诉AI做什么;现在你是“架构师”,只需定义好系统的边界和规则,AI就成了自动遵循规则的“施工队”。

提示:repo-scan不是开箱即用的功能。在Cursor中,你需要在Settings > AI > Repository Scanning里开启,并指定扫描范围(建议排除/target/,/node_modules/,/dist/等构建产物目录)。首次扫描可能耗时1-2分钟,但后续增量更新极快。别嫌麻烦——这是让你的经验真正“活”起来的必经步骤。

4. 从“写代码”到“写规则”:一个资深工程师的转型实操路径

明白了原理,下一步就是动手。但千万别一上来就试图写满100条规则。我带过的团队里,转型最成功的,都遵循一个极其朴素的路径:从一次真实的Code Review开始,把你的口头点评,变成第一条机器可执行的规则。

去年帮一个金融风控团队做AI辅助落地,他们的Code Review会议里,最常听到的一句话是:“这个SQL的WHERE条件,为什么没用到索引?查一下执行计划!”——这句话背后,是十年DBA经验凝结的直觉。我们把它转化成了第一条claude.md规则:

# 【规则ID: SQL-INDEX-HINT-REQUIRED】 - **触发条件**:当生成SQL语句,且WHERE子句包含`user_id = ?`、`order_id = ?`、`created_at BETWEEN ? AND ?`等常见字段时 - **必须行为**:在SQL末尾添加`/*+ INDEX(table_name index_name) */`提示,index_name必须来自以下映射: - `user_id` → `idx_user_id` - `order_id` → `idx_order_id` - `created_at` → `idx_created_at` - **错误示例**:`SELECT * FROM risk_event WHERE user_id = ? AND status = 'PENDING'` → 缺少索引提示 - **正确示例**:`SELECT * FROM risk_event WHERE user_id = ? AND status = 'PENDING' /*+ INDEX(risk_event idx_user_id) */`

这条规则上线后,团队发现两个意外收获:第一,AI生成的SQL,索引命中率从62%提升到98%;第二, junior工程师开始主动查EXPLAIN,因为他们发现“不加提示的SQL,AI根本不会生成”。

这就是转型的核心心法:不要追求规则的“完整性”,而要追求规则的“疼痛感”。每一条规则,都应该对应你曾经深夜救火、或者Code Review时拍桌子指出的那个具体问题。以下是我在多个项目中验证过的四步实操法:

4.1 第一步:建立“痛点-规则”映射表(1小时)

拿出最近三个月的Git提交记录,筛选出被反复驳回的PR,或者你亲自修复的线上Bug。创建一个简单表格:

日期PR链接问题描述你的口头点评领域关键词是否可结构化
2024-03-15#287Redis缓存穿透导致DB雪崩“查不到的数据也要写空值,加个短过期”cache, redis, null-value
2024-02-22#211HTTP超时未设置,下游抖动引发级联失败“所有FeignClient必须设connectTimeout=2000, readTimeout=5000”http, timeout, feign
2024-01-08#144日志泄露用户手机号“手机号必须脱敏,显示为138****1234”log, pii, mask

重点看最后一列“是否可结构化”。如果问题能用“字段名+操作+值”描述(如“手机号字段必须脱敏”),就标记✅;如果依赖复杂上下文(如“这个算法的时间复杂度太高”),先放一边,等后面有更多数据再抽象。

4.2 第二步:编写第一条“最小可行规则”(30分钟)

选表格里第一个✅项,按前文说的四元组格式写。记住三个铁律:

  • 触发条件必须具体:用文件路径、类名、方法签名、SQL关键字等硬指标,别用“当处理用户数据时”这种模糊描述;
  • 必须行为要可验证:写出AI生成的正确代码示例,确保它能被静态扫描工具(如ESLint、Checkstyle)直接检测;
  • 错误示例要真实:直接复制PR里被驳回的那行代码,让它成为AI的“反面教材”。

4.3 第三步:在真实场景中验证并迭代(每天15分钟)

把规则放进claude.md,然后刻意用Cursor生成一个会触发它的场景(比如在用户服务里写一个查询方法)。观察AI输出:

  • 如果它遵守了规则,记录“有效”;
  • 如果它部分遵守(比如加了索引提示但写错了表名),在规则里补充table_name必须来自当前SQL的FROM子句
  • 如果它完全无视,检查触发条件是否太宽泛(比如WHERE user_id = ?匹配到了所有SQL),收紧条件。

关键技巧:把AI当成实习生,每次它犯错,不是删掉规则,而是给它写一份更详细的“实习指导手册”。我见过最有效的规则,是这样写的:

# 【规则ID: LOG-PII-MASK】 - **触发条件**:当生成日志语句(包含`logger.info(`, `log.debug(`等),且日志内容字符串中包含`phone`, `mobile`, `tel`, `contact`等关键词时 - **必须行为**:对匹配到的手机号字段,调用`MaskUtils.maskPhone(phoneNumber)`方法,且该方法必须已存在于`/common/utils/`包下 - **错误示例**:`logger.info("user phone: {}", user.getPhone())` → 未脱敏 - **正确示例**:`logger.info("user phone: {}", MaskUtils.maskPhone(user.getPhone()))` - **兜底策略**:如果`MaskUtils`类不存在,则必须先生成该工具类,包含以下方法: ```java public class MaskUtils { public static String maskPhone(String phone) { if (phone == null || phone.length() < 11) return phone; return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); } }
这个“兜底策略”,就是把你的经验预判变成了AI的默认行为。 ### 4.4 第四步:建立团队规则演进机制(每周30分钟) 规则不是写完就扔那儿。我们要求每个团队每周做一次“规则健康度检查”: - 统计本周AI生成代码中,该规则被触发的次数; - 统计被规则拦截的“错误生成”数量; - 收集工程师反馈:“这条规则有没有误伤?有没有漏掉的场景?” 然后用一个简单的公式计算ROI: `规则价值 = (拦截的错误数 × 平均修复成本) - (规则维护时间 × 工程师时薪)` 只要结果为正,就继续投入。我们有个团队,第一条规则上线三周后,ROI达到1:7——意味着每花1小时维护规则,节省了7小时的Bug修复时间。这时,大家才真正相信:**写规则,不是增加负担,而是把你的经验,变成了永不疲倦的自动化守门员。** > 最后分享一个血泪教训:别在`claude.md`里写“禁止硬编码密码”。这毫无意义。AI根本分不清什么是密码、什么是API Key、什么是数据库连接串。真正有效的是:“当生成`DataSource`配置时,`password`属性必须从`Environment.getProperty("db.password")`获取,且该属性必须在`application-secret.yml`中定义”。**具体,才是AI唯一能听懂的语言。**