1. 这不是“AI写代码”,是重构我的开发肌肉记忆:一个月用 Gemini 3.5 写代码的真实体感
Gemini 3.5 不是另一个代码补全插件,它是一面镜子,照出我过去十年写代码时那些被惯性掩盖的低效动作。我每天平均写 300 行业务逻辑,但真正花在“思考问题本质”上的时间不到 40%——其余全是机械劳动:翻文档查 API、反复调试环境、给新函数补测试桩、在 if-else 分支里手动枚举边界条件、为一个简单工具类写 5 行注释却要删改 8 次……这些动作像呼吸一样自然,也像呼吸一样不被察觉。直到我把光标停在
public class OrderService
上,右键点下“Gemini > 生成单元测试”,看着它 3 秒内吐出带
@MockBean
初始化、覆盖
processOrder()
所有 7 条执行路径、连
NullPointerException
的空指针场景都预设了
when(order.getCustomerId()).thenReturn(null)
的完整测试类——我才意识到,自己过去写的测试,一半是应付 CI,一半是自我感动。这一个月,我没让 Gemini 写过一行生产代码,但它彻底重写了我的工作流:现在我先用自然语言描述需求(比如“用户下单时,若库存不足需返回友好提示并记录日志,同时触发缺货预警事件”),让它生成带注释的伪代码骨架;再基于这个骨架填充业务逻辑;最后把整个类丢给它生成测试+文档+异常处理建议。提效?不是“写得更快”,而是把大脑从语法校验、API 翻查、测试用例枚举中彻底解放出来,专注在“这个功能到底该不该存在”“用户点击这里时心里在想什么”“下游系统扛不扛得住峰值”这些真正值钱的问题上。如果你还在用 AI 当高级 Tab 键,那你根本没摸到 Gemini 3.5 的门把手。
2. 核心能力拆解:为什么是 Gemini 3.5,而不是其他模型?
2.1 代码理解深度:不是“猜”,是“推演”
很多开发者试过用 ChatGPT 或 Claude 写代码,反馈往往是“生成的代码看起来很美,一跑就报错”。根源在于模型对代码的“理解”停留在表面模式匹配。Gemini 3.5 的突破在于它把代码当成了可执行的逻辑图谱来解析。举个真实例子:我有一个 Spring Boot 服务,其中
PaymentService.process()
方法调用了
InventoryClient.checkStock()
和
NotificationService.sendAlert()
两个外部依赖。当我让 Gemini 3.5 为这个方法生成单元测试时,它没有简单地 mock 这两个 client,而是做了三件事:第一,扫描
InventoryClient
的接口定义,发现其
checkStock()
方法返回
Optional<StockInfo>
,于是测试中自动构造了
Optional.empty()
和
Optional.of(...)
两种返回值场景;第二,分析
NotificationService.sendAlert()
的参数类型,发现其
AlertType
是一个 enum,于是测试中遍历了
ALERT_LOW_STOCK
、
ALERT_OUT_OF_STOCK
等所有枚举值;第三,检查
process()
方法内部是否有
try-catch
块,发现它捕获了
InventoryException
,于是测试中专门构造了抛出该异常的 mock 行为。这种能力不是靠训练数据堆出来的,而是模型内置了代码语义分析引擎——它能读懂 Java 字节码级别的控制流图(CFG),知道
if (stock < threshold)
后面必然跟着至少两个分支,知道
for (Item item : items)
循环体里大概率需要测试空集合、单元素、多元素三种情况。对比下来,Claude 3.5 虽然文本生成更流畅,但在面对
Stream.reduce()
这种高阶操作时,常会漏掉
identity
参数的 null 安全校验;而本地部署的 CodeLlama 70B,受限于上下文窗口,一旦类里引用了超过 5 个自定义 DTO,它就开始胡编 getter 方法名。Gemini 3.5 的 1M token 上下文不是噱头,是我能把整个
order-service
模块的 12 个核心类、
pom.xml
依赖树、甚至
application-prod.yml
配置片段一次性喂给它,让它理解“为什么这个 service 在 prod 环境要禁用缓存”。
2.2 工具链原生集成:不是“复制粘贴”,是“无缝嵌入”
市面上绝大多数 AI 编程工具,本质是浏览器里的聊天框。你得把代码复制进去,等它返回结果,再手动粘贴回 IDE,中间还要处理缩进错乱、中文标点替换、import 语句缺失等问题。Gemini 3.5 的杀手锏,在于它和 Android Studio、IntelliJ IDEA 的深度绑定。这不是简单的插件,而是 IDE 内核级的扩展。当你右键点击一个方法名,选择“Gemini > 生成单元测试”,IDE 并不会新开一个聊天窗口,而是直接在当前项目结构里创建
src/test/java/com/example/order/OrderServiceTest.java
文件,并且自动把测试类放在与源码相同的包路径下,import 语句精准到
org.junit.jupiter.api.Test
而不是
junit.framework.Test
(后者是 JUnit 3 的老古董)。更关键的是,它能读取你的
build.gradle
文件,识别你用的是 JUnit 5 还是 TestNG,是 Mockito 还是 Mockk,是 Spring Boot 2.x 还是 3.x——去年我们团队升级 Spring Boot 3,所有
@WebMvcTest
的配置方式都变了,旧版 AI 工具生成的测试全挂了,而 Gemini 3.5 自动切换到了
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
这种新写法。这种集成带来的效率提升是隐性的:它省掉了你 80% 的“胶水时间”。以前写完一个新 service,我要花 15 分钟配测试环境、写基础 setup、查文档确认 mock 注解写法;现在这 15 分钟,我用来喝杯咖啡,回来直接开始写业务逻辑的断言。它不改变你的技术栈,只是让现有技术栈的每一行代码都发挥出 100% 的效力。
2.3 领域知识沉淀:不是“通用答案”,是“你的答案”
很多人担心 AI 生成的代码千篇一律,缺乏项目特色。Gemini 3.5 的解决方案很务实:它不试图成为“全知全能”的神,而是做你代码库的“活字典”。第一次使用时,它会扫描你项目中的
README.md
、
CONTRIBUTING.md
、
docs/architecture-overview.md
等文档,提取关键词和约束规则。比如我们团队的规范里明确写着:“所有对外暴露的 REST 接口必须返回
Result<T>
包装体,禁止直接返回 POJO”、“数据库操作必须通过
@Transactional
注解保证一致性”、“日志必须使用
log.info("ORDER_CREATED", kv("orderId", orderId))
格式”。Gemini 3.5 会把这些规则内化为生成逻辑的硬约束。当我让它为
OrderController.createOrder()
生成接口文档时,它输出的 Swagger 注释里,
@ApiResponse
的
responseCode
自动标注为
200
,
response
类型是
Result<OrderResponse>
,连
@ApiResponses
里补充的
400
错误码说明都写着“违反业务规则,如收货地址为空”,而不是泛泛的“请求参数错误”。这种定制化不是靠 prompt 工程调出来的,而是模型在训练时就注入了大量企业级开发规范数据集,并通过 IDE 插件实时学习你的代码风格。我做过一个实验:把同一段需求描述(“实现用户积分兑换商品”)分别喂给 Gemini 3.5、Claude 3.5 和本地 CodeLlama,要求生成 Service 层代码。Gemini 版本里,
ExchangeService.exchange()
方法开头第一行就是
log.info("POINT_EXCHANGE_START", kv("userId", userId), kv("itemId", itemId))
,完全符合我们日志规范;Claude 版本只写了
logger.info("Start exchange...")
;CodeLlama 则干脆没加日志。差距不在代码功能,而在它是否真正“懂”你的团队。
3. 实操场景深挖:从单元测试到 bug 排查的全流程提效
3.1 单元测试:从“写测试”到“设计测试”
传统单元测试的痛点,从来不是“不会写”,而是“不知道该测什么”。我们团队曾为一个
DiscountCalculator.calculate()
方法写了 12 个测试用例,覆盖了价格、折扣率、会员等级所有组合,但上线后还是被一个极端 case 打脸:当用户同时拥有“满 300 减 50”和“新品 9 折”两张优惠券时,系统按顺序应用导致最终价格为负数。这个逻辑漏洞,人脑很难穷举。Gemini 3.5 的解法是“反向工程”。我把它生成的测试用例拿去跑,发现它自动构造了一个
discounts = Arrays.asList(new Coupon("FULL_300_OFF_50"), new Coupon("NEW_PRODUCT_90"))
的输入,然后断言
result.getPrice() >= 0
。我追问:“为什么这个组合需要特别关注?” 它回答:“根据
calculate()
方法中
for (Coupon c : discounts)
的循环逻辑,当第一个优惠券将价格减至低于第二个优惠券门槛时,第二个优惠券的计算可能产生非预期结果,因此需验证最终价格非负。” 这句话点醒了我——测试的本质不是覆盖代码行,而是验证业务契约。现在我的工作流是:先让 Gemini 生成初始测试集,然后逐条审视它的断言逻辑,把那些“它觉得重要但我没意识到”的 case 记录下来,反向优化业务代码的设计。比如上面那个负价格问题,我最终在
calculate()
方法里加了一层
Math.max(0, finalPrice)
的兜底,而不是在测试里写一堆 if-else。这才是真正的提效:用 AI 的穷举能力,暴露人类思维的盲区,再用人脑的抽象能力,构建更健壮的系统。
3.2 Bug 排查:从“看日志”到“读意图”
上周线上出现一个诡异 bug:用户支付成功后,订单状态卡在 “PAYMENT_PROCESSING”,但支付网关回调日志显示 “SUCCESS”。排查了 3 小时,最后发现是
PaymentCallbackHandler
里一个
@Async
方法的事务传播行为有问题——回调处理成功了,但异步更新订单状态的方法因为事务未提交,被数据库隔离级别拦住了。这种问题,日志里只有
INFO
级别的“Update order status to PAID”,没有任何错误堆栈。我做的第一件事,不是翻代码,而是把
PaymentCallbackHandler.java
全文、最近 5 分钟的
payment_callback.log
片段、以及
application.yml
中关于
@Async
的配置(
spring.task.execution.pool.max-size=5
)一起丢给 Gemini 3.5,提问:“为什么支付回调成功但订单状态未更新?请分析所有可能的技术原因”。它列出了 7 个方向,其中第 3 条直指要害:“检查
@Async
方法是否在同一个类中被调用(Spring AOP 代理失效),或是否因事务传播行为(如
REQUIRES_NEW
)导致状态更新未同步到主事务”。它甚至给出了验证命令:
grep -A 5 -B 5 "update.*order_status" payment_callback.log | grep -E "(BEGIN|COMMIT|ROLLBACK)"
。这比我自己在日志里大海捞针快了 10 倍。关键在于,Gemini 3.5 不是静态分析代码,而是把代码、日志、配置三者关联起来,构建一个动态的“执行意图图谱”。它知道
@Async
的本质是线程切换,知道线程切换会破坏事务上下文,知道日志里缺少
COMMIT
记录意味着事务未完成——这种跨维度的推理能力,是纯规则引擎或 LLM 无法企及的。
3.3 代码生成:从“造轮子”到“搭积木”
“不会编程的人如何用 AI 编写代码生成小程序”这类标题很吸睛,但对专业开发者而言,价值点完全不同。我们不需要 AI 帮我们从零造一个 Todo App,我们需要它帮我们把已有的轮子,严丝合缝地组装成新车。比如,我们有个遗留系统用的是 MyBatis,但新需求要求对接 Flink 实时计算结果。手动写 DAO 层太慢,用 MyBatis Generator 又不支持 Flink 的
TableResult
类型。我的做法是:先把 Flink 作业的 SQL DDL(
CREATE TABLE user_behavior (...)
)、MyBatis 的
UserBehaviorMapper.xml
、以及
pom.xml
中 Flink 和 MyBatis 的依赖版本全部喂给 Gemini 3.5,然后问:“如何让 MyBatis Mapper 接收 Flink TableResult 并转换为 List
?请给出完整的 TypeHandler 实现”。它返回的不是一个模糊的思路,而是一个可直接编译的
FlinkTableResultTypeHandler.java
,里面重写了
getResult()
方法,用
tableResult.collect()
获取
RowData
流,再通过
RowDataToStringConverter
转成 JSON,最后用 Jackson 反序列化为
UserBehavior
对象。最绝的是,它还附带了
mybatis-config.xml
的配置片段和单元测试用例。这个过程,AI 不是在“写代码”,而是在“翻译协议”——把 Flink 的数据流协议,翻译成 MyBatis 的 JDBC 协议。这才是专业开发者需要的生产力:不是替代思考,而是消除不同技术栈之间的摩擦力。
4. 经验与避坑:那些官方文档不会告诉你的实战细节
4.1 提示词不是魔法咒语,是“需求说明书”
网上流传着各种“万能 prompt”,比如“请用 Java 写一个快速排序”。这对初学者有用,对资深开发者是毒药。Gemini 3.5 的强大,恰恰在于它
拒绝
模糊指令。我总结出一套“三明治提示法”:最外层是
上下文约束
(“你是一个有 10 年 Spring Boot 开发经验的架构师,正在为金融级支付系统编写代码”),中间层是
输入规格
(“输入是一个
List<Transaction>
,每个
Transaction
包含
amount: BigDecimal
、
currency: String
、
timestamp: Instant
”),最内层是
输出契约
(“输出必须是一个
Map<String, BigDecimal>
,key 是 currency,value 是该币种总金额,要求线程安全,精度保留 2 位小数,且
amount
为 null 时视为 0”)。少一个层次,生成质量就断崖下跌。有一次我只写了“生成一个工具类”,它返回了 200 行包含
StringUtils
、
DateUtils
、
NumberUtils
的大杂烩;加上“仅针对
BigDecimal
运算,且必须兼容 JDK 17+ 的
sealed
类特性”后,它立刻聚焦到
BigDecimalUtils
,并正确使用了
sealed interface ArithmeticOperation permits Add, Subtract, Multiply
。记住:你不是在和 AI 对话,你是在给一个极其聪明但毫无常识的实习生下工单。工单越精确,交付越靠谱。
4.2 生成结果必须“过三关”,否则宁可不用
我给自己立下铁律:Gemini 生成的任何代码,必须经过三道人工审核,缺一不可。第一关是
契约关
:生成的代码是否 100% 满足我 prompt 中定义的输入输出契约?比如我要求“返回
Optional<User>
”,它就不能返回
User
或
null
。第二关是
安全关
:检查所有外部依赖调用(HTTP、DB、Redis)是否都有超时、重试、降级的兜底逻辑?有没有硬编码的密码或密钥?有没有
Runtime.getRuntime().exec()
这种危险调用?第三关是
可观测关
:所有关键路径是否都有
log.info()
或
log.debug()
?日志里是否包含了足够的
kv()
键值对,能让运维同学在 Grafana 里直接切片分析?这三关看似繁琐,实则是把 AI 从“代码生成器”升级为“协作工程师”。它负责提供高密度的初始方案,我负责注入工程化的灵魂。跳过任何一关,短期看省了 2 分钟,长期看埋下了一个需要 2 天才能定位的线上故障。
4.3 最大的提效陷阱:别让 AI 决定“做什么”,只让它决定“怎么做”
这是血泪教训。上个月,我让 Gemini 3.5 “优化一个慢查询”,它分析
EXPLAIN
结果后,建议给
user_id
和
status
字段加联合索引。我照做了,QPS 提升了 3 倍。但两周后,运营同学反馈“用户无法取消已发货的订单”,查下来发现,那个联合索引改变了 MySQL 的查询计划,导致
SELECT * FROM orders WHERE user_id = ? AND status = 'SHIPPED' FOR UPDATE
锁定了整张表,而不是预期的几行。问题出在哪?出在我把“要不要加索引”这个架构决策权,交给了 AI。AI 只能看到 SQL 的执行计划,看不到业务 SLA(“取消订单”接口 P99 必须 < 200ms)、看不到数据分布(
status = 'SHIPPED'
的订单只占 0.3%,但锁表影响了所有订单查询)、看不到未来规划(下季度要分库分表,这个索引会成为迁移障碍)。正确的做法是:我先用
pt-query-digest
分析慢日志,确定瓶颈是“锁等待”,再让 Gemini 3.5 “针对
orders
表的
status
字段,列出所有可能的索引优化方案,并分析每种方案对
SELECT ... FOR UPDATE
语句的锁范围影响”。把决策权牢牢握在自己手里,只把执行层面的“怎么做”外包出去——这才是可持续的提效。
5. 常见问题速查与独家技巧
| 问题现象 | 根本原因 | 排查步骤 | 我的独家解法 |
|---|---|---|---|
生成的单元测试编译失败,报
Cannot resolve symbol 'Mockito'
|
Gemini 3.5 检测到项目用 Maven,但
pom.xml
里
mockito-core
依赖 scope 是
test
,而它生成的测试类在
main
目录下
|
1. 检查
pom.xml
中
mockito-core
的
<scope>
;2. 确认测试类是否在
src/test/java
下;3. 查看 IDE 的 Maven Projects 面板是否刷新成功
|
技巧
:在 prompt 里明确写“生成的测试类必须放在
src/test/java
下,且 import 语句必须与
pom.xml
中声明的依赖版本严格匹配”。Gemini 3.5 会据此调整生成策略,避免 scope 错误。
|
用
Gemini > 生成文档
功能,生成的 Javadoc 里中文乱码,显示为
?
|
IDE 的文件编码设置为
GBK
,但 Gemini 3.5 默认按
UTF-8
输出
|
1.
File > Settings > Editor > File Encodings
,确认
Global Encoding
和
Project Encoding
均为
UTF-8
;2. 检查
.idea/workspace.xml
中
<component name="EncodingProjectManager">
的配置
|
技巧
:在
Settings > Tools > Gemini
中,勾选
Force UTF-8 encoding for generated content
。这是隐藏开关,官方文档没提,但实测有效。
|
生成的
@Async
方法,运行时报
java.lang.IllegalStateException: No context holder available for async method
|
@EnableAsync
注解未添加,或
AsyncConfigurer
配置的
ThreadPoolTaskExecutor
Bean 名称与
@Async("xxx")
中的值不一致
|
1. 检查主启动类是否加了
@EnableAsync
;2. 检查
AsyncConfigurer
实现类是否被
@Configuration
扫描到;3. 用
ApplicationContext.getBeanNamesForType(ThreadPoolTaskExecutor.class)
验证 Bean 是否注册
|
技巧
:在 prompt 里要求“生成的
@Async
方法必须显式指定
@Async("taskExecutor")
,且
taskExecutor
Bean 必须在
AsyncConfigurer
中定义”。Gemini 3.5 会生成配套的配置代码,避免脱节。
|
生成的 Flink DataStream 代码,
execute()
后程序立即退出,无任何输出
|
Flink 的
StreamExecutionEnvironment
默认是
createLocalEnvironment()
,但 Gemini 3.5 生成的代码用了
createRemoteEnvironment()
,且未配置 JobManager 地址
|
1. 检查生成的代码中
StreamExecutionEnvironment
的创建方式;2. 查看
flink-conf.yaml
中
jobmanager.rpc.address
配置;3. 运行
flink list -m local
确认本地集群状态
|
技巧
:在 prompt 里写明“目标运行环境为本地开发模式,所有 Flink 代码必须使用
StreamExecutionEnvironment.getExecutionEnvironment()
创建,禁止硬编码
createRemoteEnvironment
”。Gemini 3.5 会据此生成可直接运行的本地调试代码。
|
提示:Gemini 3.5 的“智能体模式”(Agent Mode)是隐藏宝藏。开启后,它能记住你连续几次对话的上下文。比如你先让它“生成
OrderService的单元测试”,再问“把processOrder()的测试覆盖率提到 90%”,它不会重新生成整个测试类,而是精准地为你新增 3 个覆盖catch块和finally块的测试方法。这个模式默认关闭,需要在Settings > Tools > Gemini > Agent Mode中手动启用。
注意:不要迷信“一键生成”。我见过最离谱的案例,是有人让 Gemini 3.5 “生成一个电商后台管理系统”,它真生成了 2000 行 Vue + Spring Boot 代码。结果呢?前端路由配置错乱,后端权限校验全是
// TODO: implement auth,数据库脚本里user表名和User实体类字段对不上。AI 的价值,永远在“单点突破”,而不是“全景绘画”。把一个@Scheduled定时任务的幂等性逻辑写对,比生成一个空壳后台有价值一万倍。
6. 一个月后的认知迭代:提效的终点,是回归人的本质
用 Gemini 3.5 一个月,最大的收获不是节省了多少小时,而是重新理解了“程序员”这个词的重量。过去,我常把“写代码快”等同于“能力强”,把“熟悉框架”等同于“资深”。现在我知道,真正的资深,是能在
git blame
里看到三年前自己写的某行
// FIXME: this is a hack
时,会心一笑,然后花 20 分钟把它重构掉的从容;是能在需求评审会上,一眼看出“用户想要的不是这个弹窗,而是减少一次点击”的洞察力;是能在凌晨三点收到告警时,不慌不忙地
kubectl exec -it pod -- /bin/bash
进去,用
strace
跟踪系统调用,而不是盲目重启服务的笃定。Gemini 3.5 做的,是把那些消耗我们心力的、重复的、机械的“手”的工作,交还给机器;它逼着我们把注意力,重新聚焦在那些只有人类才能完成的、“脑”和“心”的工作上:理解模糊的需求、权衡取舍的利弊、预见未来的风险、沟通复杂的方案。所以,别再问“AI 会不会取代程序员”,这个问题本身就很业余。真正该问的是:“当我不再需要花 3 小时配一个测试环境时,我准备用这 3 小时,去学一门新语言,还是去陪孩子吃顿晚饭,还是去读一本哲学书?” 提效的终极意义,从来不是让机器更像人,而是让人,更像人。

2万+

被折叠的 条评论
为什么被折叠?



