AI时代的程序员修养:接手 AI 写的代码,先别急着骂
《AI时代的程序员修养》前面两篇讲了测试和可观测性。测试让结果可复现观测让系统出事时可定位。这一篇讲更现实的场景你接手了一段 AI 写出来的代码已经能跑甚至已经有人在用。但它像一团线函数很长边界不清异常处理玄学改一处容易带出三处问题。这时先别急着骂。骂没有产出。真正有产出的是把它拆开、钉住、清理、重构让它从“能演示的原型”变成“能继续维护的代码”。不要一上来大改接手烂代码最危险的动作是立刻重写。AI 生成代码尤其如此。它可能有很多问题但也可能藏着一些已经被用户验证过的业务细节。你觉得多余的分支可能是在绕某个真实线上坑你觉得奇怪的字段可能被前端或者脚本依赖你觉得可以合并的函数可能实际上承担了不同副作用。所以第一步不是重构而是冻结行为。冻结行为的意思是先确认它现在到底做了什么再决定哪些要保留、哪些要删除、哪些要重写。一个简单原则没有测试保护的重构不叫重构叫赌博。先跑起来拿到基线接手 AI 代码先做三件事。能启动 能测试 能复现关键路径不要急着整理目录也不要急着改命名。先让项目在本地跑起来记录启动命令、环境变量、依赖版本和最小数据集。例如一个报告生成服务可以先建立这样的基线uv sync uv run pytest uv run uvicorn app.main:app --host 0.0.0.0 --port 8000再准备一组最小请求curl -X POST http://localhost:8000/reports \ -H content-type: application/json \ -d {tool_id:demo_tool,query:最近一周数据,format:summary}如果这些都跑不起来先补运行文档和 fixture。一个不能稳定启动的项目谈不上重构。AI 生成代码常见的问题是本地能跑一次但依赖隐含在全局环境里。接手时要把这些隐含条件显式化.env.example、测试数据、启动脚本、数据库迁移、外部服务 mock。用回归测试钉住现状AI 代码不可靠不能只靠读。读代码只能告诉你作者想表达什么测试才能告诉你系统实际承诺什么。接手阶段的测试不追求优雅先追求保护关键行为。比如报告服务至少要有这些回归测试def test_create_report_returns_queued(client): response client.post( /reports, json{tool_id: demo_tool, query: hello, format: summary}, ) assert response.status_code 200 assert response.json()[status] queued assert report_id in response.json() def test_create_report_rejects_unknown_tool(client): response client.post( /reports, json{tool_id: missing, query: hello, format: summary}, ) assert response.status_code 400 assert response.json()[error_code] TOOL_NOT_FOUND这类测试不一定完美但它能先保护入口行为。然后再补 bug 样例、边界样例和失败样例空 query 怎么处理 超长 query 怎么处理 重复提交是否幂等 第三方超时是否进入重试 权限不足是否返回稳定错误码 结果保存失败是否会重复扣费接手 AI 代码时测试不是为了证明你写得好而是为了防止你改坏现有行为。画出模块图很多 AI 代码难维护不是因为语法差而是因为边界糊。一个函数里可能同时做了参数校验、权限检查、数据库查询、第三方调用、结果格式化、扣费、日志、埋点。它能跑但任何改动都可能牵一大片。接手时可以先画一个粗模块图API Layer - Auth / Permission - Use Case / Application Service - Domain Rules - Repository - Provider Client - Queue / Worker - Billing / Audit这不是为了套架构名词而是为了判断职责在哪里。更具体一点API 层只处理 HTTP、schema、错误响应。 Use Case编排业务流程例如创建报告、取消报告。 Domain Rules纯业务规则例如状态机、金额、权限策略。 Repository数据库读写不包含业务决策。 Provider Client外部接口调用包含超时、重试、错误映射。 Worker异步执行不直接依赖 HTTP 上下文。 Audit/Billing关键副作用必须可追溯、可幂等。AI 生成代码常把这些揉成一个大函数。重构的目标不是“分层好看”而是让每个改动只影响它该影响的地方。看依赖方向判断代码是否难维护一个很实用的方法是看依赖方向。理想情况下核心业务规则应该少依赖外部东西。它可以被单测直接调用不需要启动数据库、HTTP 服务或第三方 SDK。例如def can_cancel_report(status: str, owner_id: str, actor_id: str) - bool: if actor_id ! owner_id: return False return status in {queued, running}这类函数最好是纯函数。输入明确输出明确没有数据库没有网络没有时间读取没有随机数。反过来下面这种代码就难测async def cancel_report(report_id: str, actor_id: str): report await db.get(report_id) user await auth.current_user() if user.id ! report.owner_id: await logger.warning(permission denied) return False await provider.cancel(report.provider_job_id) await db.update(report_id, {status: cancelled}) await send_notification(user.id) return True它混合了数据库、认证、权限规则、外部调用、状态变更和通知。要测试一个简单权限规则也要拖进一堆依赖。接手时可以先把纯规则抽出来def decide_cancel_report(report: Report, actor: Actor) - CancelDecision: if actor.id ! report.owner_id: return CancelDecision(allowedFalse, reasonnot_owner) if report.status not in {queued, running}: return CancelDecision(allowedFalse, reasoninvalid_status) return CancelDecision(allowedTrue, reasonok)外层再处理副作用decision decide_cancel_report(report, actor) if not decision.allowed: raise PermissionDenied(decision.reason) await provider.cancel(report.provider_job_id) await repository.mark_cancelled(report.id) await audit.record(...)这一步通常是 AI 代码从“能跑”变成“能测”的关键。圈复杂度和函数长度不是玄学重构时可以用一些很朴素的指标做雷达。函数太长、分支太多、嵌套太深通常意味着它承担了太多职责。圈复杂度可以粗略理解为代码里有多少条独立路径。if、for、while、except、逻辑运算都会增加路径数量。路径越多测试覆盖越难修改风险越高。比如def normalize_result(data, mode, user_type, fallback): if mode summary: if user_type vip: ... else: ... elif mode table: if fallback: ... else: ... elif mode chart: ... else: ...这类函数不一定错但很容易变成分支垃圾场。接手 AI 代码时可以先找这些信号单个函数超过 80-100 行 嵌套超过 3 层 一个函数里出现多种外部依赖 异常处理分散在多个层级 同一个状态字段被多个模块随手改 大量布尔参数控制行为这些不是绝对规则但足够帮你找风险点。扇入扇出决定改动风险还有两个很实用的概念扇入和扇出。扇入是“有多少地方调用我”。扇出是“我调用了多少东西”。高扇入很多地方依赖它改它要小心。 高扇出它依赖很多东西通常职责过重。 高扇入 高扇出危险核心先补测试再动。例如一个execute_tool()函数被 API、worker、定时任务、脚本都调用同时内部又访问数据库、缓存、第三方接口、扣费、日志、埋点。这种函数就是重构雷区。处理方法不是一刀切重写而是先加保护层1. 给现有 execute_tool 写黑盒回归测试。 2. 记录输入输出和关键副作用。 3. 把内部纯规则逐步抽出。 4. 把外部依赖包成接口。 5. 新逻辑走新路径旧逻辑保留一段时间。这就是小步重构。先隔离副作用AI 代码最大的问题之一是副作用到处飞。副作用包括写数据库 发 HTTP 请求 发消息队列 扣费 发通知 写文件 读当前时间 读随机数 写审计这些操作不是不能有而是不能散落在任何地方。一个可维护的流程通常应该把“决策”和“执行副作用”分开def plan_report_execution(request: ReportRequest) - ExecutionPlan: # 纯决策选择 provider、检查模式、计算预算、生成步骤 return ExecutionPlan(...) async def execute_report_plan(plan: ExecutionPlan, deps: Dependencies) - ReportResult: # 副作用集中在这里调用 provider、写数据库、写审计 ...这样做有两个好处。纯决策可以大量单测跑得快也不依赖外部环境。副作用集中后可以统一处理超时、重试、幂等、错误映射和审计。给 AI 的提示词里要明确这一点请把业务决策写成纯函数不要在纯函数里访问数据库、网络、当前时间或随机数。 外部副作用通过明确的 dependency interface 注入。删代码比改代码更重要AI 很擅长生成“看起来有用”的代码。备用分支、未使用参数、过度抽象、未来可能用到的扩展点、重复的错误包装、没人调用的 helper都可能堆在项目里。接手时要敢删但要有证据。可以按这个顺序处理搜索调用点确认没有引用。 跑测试和类型检查。 看构建产物或路由注册。 如果是 API 或脚本入口确认没有外部调用。 删除后保留一次独立提交方便回滚。有些代码不能只靠本仓库搜索判断例如被定时任务、外部脚本、用户 webhook 调用的入口。这类要先标记 deprecated打日志观察一段时间再删除。删除代码的收益很直接上下文变短AI 更不容易被旧逻辑带偏人 review 也更轻。重构要按接缝切重构不是把文件重新摆一遍。好的重构会沿着接缝切。接缝就是系统里天然可以分开的地方接口、数据库访问、外部 provider、队列任务、状态机、权限策略。例如一个混乱的执行函数execute() - parse input - check permission - select provider - call provider - normalize result - save history - bill user - emit telemetry可以先切成parse_execute_request() authorize_execute() select_provider() call_provider() normalize_provider_result() record_execute_history() charge_usage() emit_execute_events()注意不是每个函数都要变成一个类。很多时候几个清楚的函数就够了。AI 生成代码经常过度类化。你让它“优化架构”它可能立刻生成一堆 manager、handler、service、factory。接手时要压住这个冲动先把边界切清楚再决定是否需要抽象。用 Strangler Fig 迁移大块逻辑如果旧逻辑太大不适合一次性重写可以用 Strangler Fig 的思路新逻辑一点点包住旧逻辑逐步替换。做法大概是保留旧入口。 在入口前加一个 router。 小流量或指定场景走新实现。 新旧结果并行比较一段时间。 确认一致后扩大范围。 确认稳定后删除旧实现。伪代码async def execute(request: ExecuteRequest) - ExecuteResult: if should_use_new_executor(request): return await new_executor.execute(request) return await legacy_executor.execute(request)如果是纯计算逻辑还可以影子执行legacy_result await legacy_executor.execute(request) if shadow_enabled(request): new_result await new_executor.execute_shadow(request) compare_and_record(legacy_result, new_result) return legacy_result这种方法适合大系统。它慢但风险小。AI 代码越是已经上线越不应该用“我重写一版肯定更好”的心态处理。每次重构都要能解释收益重构不是审美活动。每次重构最好能说清楚收益把纯规则抽出来单测从集成测试降为毫秒级。 把 provider client 封装起来外部错误映射统一。 把扣费逻辑移到独立模块避免重复扣费。 删除未使用分支减少 400 行无效上下文。 拆开大函数让权限、执行、记录三类失败可以分别定位。如果说不清收益就先别动。工程里有一种常见错误把“不喜欢的代码”都叫技术债。真正的技术债是它会影响交付速度、稳定性、成本或团队理解。否则只是个人偏好。接手 AI 代码尤其要克制。AI 可能写得不符合你的习惯但只要边界清楚、测试充分、可观测性完整也未必需要马上重写。Code Review 要看行为不只看风格审查 AI 代码不要只盯格式。格式化工具能解决的东西不值得人花太多时间。人应该看这些业务规则是否明确 失败场景是否完整 事务边界是否正确 副作用是否幂等 外部调用是否有超时 错误码是否稳定 日志指标是否足够 敏感数据是否泄露 测试是否覆盖关键路径 模块依赖是否反向AI 生成代码最容易在“看起来合理”的地方出问题。比如它会捕获所有异常然后返回成功会在数据库事务里调用慢接口会把用户输入原样写进日志会在重试时重复写历史。这些才是 review 的重点。一份接手 AI 代码的检查清单可以把下面这份清单放进项目里1. 项目能否一条命令启动。 2. 是否有最小 fixture 和本地测试数据。 3. 核心入口是否有回归测试。 4. 已知 bug 是否有失败测试。 5. 大函数是否承担多个职责。 6. 核心规则是否可以脱离数据库和网络测试。 7. 外部副作用是否集中处理。 8. 事务内是否存在慢外部调用。 9. 重试是否幂等。 10. 错误码、日志、指标、Trace 是否完整。 11. 未使用代码是否可以删除。 12. 高扇入高扇出函数是否有保护测试。 13. 新旧实现是否需要灰度或影子比较。这份清单不是一次性做完。它是接手顺序。给 AI 的重构提示词重构也可以让 AI 帮忙但提示词要限制它的动作请先不要直接重写代码。 请审查这段代码并输出 1. 当前代码的主要职责列表。 2. 入口函数、外部副作用、数据库写入、网络调用和队列操作。 3. 高风险函数函数长度、分支数量、扇入扇出、是否混合副作用。 4. 现有行为还缺少哪些回归测试。 5. 可以先抽出的纯业务规则。 6. 可以删除的未使用代码但必须说明证据。 7. 一个小步重构计划每一步都要求测试通过。 8. 哪些地方不能直接改需要灰度或影子比较。 在我确认前不要生成最终实现。这段提示词的重点是让 AI 先分析不要上来就“优化完成”。AI 很适合做代码地图、调用链梳理、重复逻辑识别、测试样例生成。真正的边界判断、上线风险和取舍仍然要由人负责。原型不是罪没人维护才是很多 AI 代码一开始都是原型。原型本身不是问题。工程里很多好东西都是从原型长出来的。问题是把原型直接当长期资产却没有测试、没有边界、没有观测、没有删除无效代码。接手 AI 代码时最好的态度不是轻蔑也不是迷信。它能跑说明它有价值。它乱说明它还没完成工程化。程序员要做的就是把价值留下把风险拆掉。这件事听起来不酷但很重要。因为未来很多系统都会带着 AI 生成的痕迹。会接手、会判断、会清理、会小步重构的人会比只会抱怨“这代码谁写的”的人更有用。

相关新闻

终极指南:FSearch - Linux桌面文件搜索的革命性工具,告别缓慢的find命令

终极指南:FSearch - Linux桌面文件搜索的革命性工具,告别缓慢的find命令

终极指南:FSearch - Linux桌面文件搜索的革命性工具,告别缓慢的find命令 【免费下载链接】fsearch A fast file search utility for Unix-like systems based on GTK3 项目地址: https://gitcode.com/gh_mirrors/fs/fsearch 你是否曾经在Linux系统…

2026/7/21 12:11:50 阅读更多 →
3分钟快速搭建家庭智能音乐中心:让小爱音箱变身免费万能播放器

3分钟快速搭建家庭智能音乐中心:让小爱音箱变身免费万能播放器

3分钟快速搭建家庭智能音乐中心:让小爱音箱变身免费万能播放器 【免费下载链接】xiaomusic 使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。 项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic 还在为音乐平台的版权限制而烦恼吗&#…

2026/7/21 12:11:50 阅读更多 →
TI OPA2277/OPA4277系列国产替代——士模CM4112/CM4114,双通道/四通道高压斩波运放

TI OPA2277/OPA4277系列国产替代——士模CM4112/CM4114,双通道/四通道高压斩波运放

CM4112/CM4114是士模推出的双通道/四通道高压斩波运放,适用于电力行波采样、高精度传感器、热电偶 / RTD 测温等微弱信号放大场景,CM4112/CM4114可Pin-to-Pin 替代OPA2277/OPA4277系列,满足国产替代需求。【产品亮点】1、供电最高电压18V&…

2026/7/21 12:10:50 阅读更多 →

最新新闻

分享一套锋哥原创的基于PyTorch的动物图像识别系统(深度学习+PyQt6+ResNet18+ImageNet+迁移学习)

分享一套锋哥原创的基于PyTorch的动物图像识别系统(深度学习+PyQt6+ResNet18+ImageNet+迁移学习)

大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于PyTorch的动物图像识别系统(深度学习PyQt6ResNet18ImageNet迁移学习) 项目介绍 随着深度学习技术的快速发展,计算机视觉在目标分类、目标检测和图像理解等领域取得了显著进展。动物图像…

2026/7/22 19:47:14 阅读更多 →
嵌入式EMIF接口实战:SDRAM与异步存储器配置与调试全解析

嵌入式EMIF接口实战:SDRAM与异步存储器配置与调试全解析

1. 项目概述在嵌入式系统开发中,处理器与外部存储器的“对话”效率,直接决定了整个系统的性能上限。无论是需要高速数据吞吐的SDRAM,还是用于存储启动代码的NOR Flash,它们与CPU之间的桥梁——外部存储器接口(EMIF&…

2026/7/22 19:47:14 阅读更多 →
如何使用Revo实现领域驱动设计(DDD):从理论到实践的终极教程

如何使用Revo实现领域驱动设计(DDD):从理论到实践的终极教程

如何使用Revo实现领域驱动设计(DDD):从理论到实践的终极教程 【免费下载链接】Revo Event Sourcing, CQRS and DDD framework for C#/.NET Core. 项目地址: https://gitcode.com/gh_mirrors/revo/Revo Revo是一个专为C#/.NET Core构建的开源框架,…

2026/7/22 19:47:14 阅读更多 →
KNN算法从理论到实践:DataAnalysisInAction手写数字识别项目解析

KNN算法从理论到实践:DataAnalysisInAction手写数字识别项目解析

KNN算法从理论到实践:DataAnalysisInAction手写数字识别项目解析 【免费下载链接】DataAnalysisInAction (Finished) Geek Time Data Analysis Practical 45 Lecture - Detailed notes containing markdown images mind map code data can be read directly code te…

2026/7/22 19:47:14 阅读更多 →
深入解析TI DDR2/3内存控制器:地址映射、性能管理与低功耗实战

深入解析TI DDR2/3内存控制器:地址映射、性能管理与低功耗实战

1. 项目概述与核心价值在嵌入式系统、高性能计算乃至我们日常使用的手机和电脑里,内存控制器(Memory Controller)扮演着“交通枢纽”和“调度中心”的关键角色。它负责将处理器发出的、看似抽象的“逻辑地址”请求,精准、高效地翻…

2026/7/22 19:47:14 阅读更多 →
045、正激变换器的磁复位技术

045、正激变换器的磁复位技术

045、正激变换器的磁复位技术 一、一个让我熬夜三天的磁复位问题 去年做一款48V转12V/20A的通信电源,选了双管正激拓扑。样机调试时,一切看似正常——输出稳定,效率88%,纹波也在范围内。但连续老化到第6个小时,MOSFET突然炸了。换上新的,又炸。拆下变压器测量,发现磁芯…

2026/7/22 19:46:14 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻