
原文:How to survive as a developer without AI code-gen and without autocomplete|译者前言:本文来自 dev.to,观点有价值,转写发布供读者参考。
嘿,大家好!我叫 Viktor,是一家电商公司的资深后端开发,负责物流团队。
今天我来聊聊我是如何在 surrounded by vibe-coders、code-writing agents 和一个 legacy 项目的环境中存活的。也会讲讲我是怎么用 LLMs 的,以及我使用神经网络的经验等等。
.claude/agents/*.md markdown 文件——可复制粘贴到任何项目。设置细节和踩坑记录在末尾。快速说明一下——我不是昨天才接触 AI 工具的人。自 2019 年起我就用过 Tabnine,然后迁移到 Cursor,再看了更新的工具。所以我对手写代码的选择——不是因为不懂,而是有意为之,为了自我提升、更深入理解项目等。
主要问题——这是一个超过 13 年的项目:代码写得不一致,函数、类和变量的使用不符合预期,其他逻辑被硬塞进去,或者它们就是死代码,标记着 TODO 或 FIXME。由于这一切加上庞大的代码库,目前我们正在剥离 domain logic,但目前还活在这样的世界里 :) 。在这样的项目上工作时,AI 模型会产生幻觉、生成不一致的代码,经常搞错。
同样重要的是——我们组织中的 devs 与他们的 domain 紧密绑定,所以部分分析工作、与 onboarding managers 和 analysts 的沟通、写文档——所有这些都需要对项目代码有非常深入的理解。这让你能够高效地研究任务、处理 incidents、预测新任务和需求的时间线。
处理代码的主要工具是代码质量工具:ruff、mypy、通过 pytest 写测试,现在还有用于项目详细 review 的 AI。我构建了一套 agents——1 个 orchestrator 和 7 个 subagents——从不同方向工作:security check、algorithm logic check、code style check、autotests check、code organization and bloat check、devops check 和 DB check。我来简要介绍每一个。
这是 orchestrator 最终报告的样子(匿名示例,来自一个真实 PR 的发现):
| ID | Sev | Aspect | File:line | Description | Fix |
|---|---|---|---|---|---|
| B1 | BLOCKER | Logic | payments/insurance.py:355 | MIN_INSURANCE_COST = 1 以主货币设置,但 provider 以分为单位 → 保险比预期低 100 倍 | 设置为 100,注释 # in cents |
| B2 | BLOCKER | Security | scripts/executor.py:57 | exec() 带完整 __builtins__,仅靠 IS_PRODUCTION 标志保护——任何 prod 配置错误 = 完全 RCE | RestrictedPython 或 subprocess + seccomp |
| B3 | BLOCKER | Migration | orders/migrations/0021_add_entity.py | AddField(entity, FK, default=None) 没有 null=True——在非空表上部署时会崩溃 | 两步走:null=True + 在单独迁移中回填 |
| H1 | HIGH | Logic/Types | feature/config.py:130 | list[int] 用于白名单,但 provider_item.id 是 str → str in list[int] 永远是 False → 功能完全 dead | 替换为 list[str] |
| H2 | HIGH | Logic/Ops | feature/service.py:102 | 空白名单时 fail-open:all(x in [] for x in set()) 是 True → gate 反转,本应放行时放行了所有内容 | 添加 if whitelist and not providers: return False |
| H3 | HIGH | Perf-DB | feature/service.py:102 | @cached_property 读取两个 OneToOne(不在 select_related 中)+ ORM fallback → 每个 item 最多 3 条 SQL,在 serializer 中每个 order 调用两次(热路径 GET /orders/) | select_related('outlet', 'delivery_info', 'ppo_info') |
| H4 | HIGH | Security | payments/processor.py:172 | 每次请求都记录完整 payload(姓名、电话、邮箱、商品)——PII 泄露到日志存储 | Mask PII 或仅记录 payment_id + amount + status |
| H5 | HIGH | AI-smell | utils/helpers.py:42 | from utils.misc import flatten——模块/符号在项目中不存在,幻觉 import | list(itertools.chain.from_iterable(items)) |
| M1 | MEDIUM | Tests | feature/tests/test_x.py:260 | Magic number expected_insurance_sum = 1——应该引用 Conf.MIN_INSURANCE_COST,这样常量改变时测试会失败 | expected_insurance_sum = Conf.MIN_INSURANCE_COST |
| M2 | MEDIUM | Logging | feature/service.py:101 | 新增拒绝分支没有日志——incident 时无法区分"provider 不在白名单"和"provider_item=None" | log_info('feature.provider_not_allowed', extra={...}) |
每个发现都包含 author + sha(来自 git blame)和一个 Verify by: 命令,但我裁剪了这些列以便表格显示。现在介绍各个 agents。
这个 agent 关注的是普通 linter 抓不到的内容。Ruff 已经标记了未使用的 imports、line length、引号和 PEP8 命名——那是 routine,自动化处理得很好。所以我在 style agent 的 prompt 中直接写:"不要重复 ruff"。它的职责是语义。例如,函数名 process_data 对读者毫无意义,但 apply_discount 则说明了一切。或者一个叫 get_user 的函数内部还更新了记录——这已经是个陷阱。或者布尔 flag 命名为 flag 而不是 is_bulk 或 should_retry。或者变量名中没有单位但实际很重要的——price 而不是 price_kopecks,timeout 而不是 timeout_ms。
除了命名,agent 还检查 signatures(5+ 个位置参数、mutable default)、重复逻辑、超过 40 行的函数,以及与项目其他部分构建方式的不一致。还有 systemically——如果同样的错误出现在 5 个地方,那不是 5 个发现,而是一个并注明"这是你 diff 中到处都是的问题"。
"不要 deploy bug"类中最重要的 agent。关注点——运行时正确性:types、algorithms、async、datetime/Decimal、错误处理。任何项目中经常出现的特定模式:
value or default 在 0、""、[]、Decimal('0.00') 时都会触发——但你本意是"if None"Decimal + floatawait,async-view 内的阻塞 requests.get,可以用 gather 的地方用了 sequential awaitexcept Exception: pass——静默错误吞没if、改变函数语义但不迁移测试每个发现必须包含行号、具体场景和 Verify by:——即可用于验证的命令或测试。没有这些,agent 被禁止写"这里可能有个 bug"——否则它开始产生幻觉、制造噪声。
标准 OWASP 加几个现代内容。经典内容——SQL/Command/Template 注入、代码或日志中的 secrets、SSRF(外部 HTTP 没有 allowlist 和 timeout)、open(user_input) 时的路径遍历、不安全反序列化(pickle、没有 SafeLoader 的 yaml、eval)、遗漏 @login_required/permission_classes、IDOR。
不太明显但很痛的内容:
now()、gen_random_uuid())的 ALTER TABLE ADD COLUMN、CREATE INDEX 没有 CONCURRENTLY、SET NOT NULL 没有两步 schema(NOT VALID + VALIDATE)、DDL 没有 lock_timeout。这不是经典意义上的"漏洞",但和 SQL 注入一样能让 prod 宕机。reqeusts vs requests)、pin 在 pre-release 版本上、降级有安全补丁的库。重要细节:agent 检查 reachability。如果危险模式仅在测试代码或未从外部 reach 的 dev 脚本中——要么 Low 要么不记录。否则你会收到大量 false positives,人们就不再使用了。
所有在负载下暴露的问题。Algorithmic complexity(可以用 set 做查找的地方出现了 O(n²))、memory(将完整数据集加载到 RAM 而不是 streaming)、async(async 中的阻塞 I/O、无限 gather 对一千个任务)、但主要的是——DB and ORM。
N+1——legacy 项目的头号问题。循环中查询、没有 select_related/prefetch_related、没有 prefetch 的 SerializerMethodField。然后——查询质量:WHERE LOWER(email) = ... 会 kill 索引、LIKE '%pattern' 也会、JSONB 过滤没有 GIN 索引、COUNT() on 大表(EXISTS 更快)、SELECT 而 .only() 可以、SELECT FOR UPDATE 没有 SKIP LOCKED 的长事务。Cache——cache key 没有 user/tenant(用户间数据泄露)、没有 TTL、cache stampede。
每个发现包含 Hot path: Yes/No/Unknown——因为一个月拉取一次的 endpoint 的 N+1 和热路径的 N+1 是不同优先级。如果 agent 无法判断——写 Unknown,不猜测。
会 break deploy 或 monitoring,或破坏向后兼容的内容。新的 required env 没有 default——break pod 启动。Migration 中 NOT NULL 没有 DEFAULT——break 零停机 deploy。不经两步 deploy 删除列——旧 pods crash。Celery task 签名变更不 fan-out migration——旧 workers 开始失败。Kafka 消息 schema 变更——旧 schema 的 consumers break。
单独——observability。重要的新逻辑没有日志、错误级别(critical 用 debug,normal 分支用 error)、新的外部调用没有 metrics 和 span、结构化日志项目中没有使用结构化日志。还有 feature flags——有没有不通过 release 就能关闭功能的方法,有没有 rollback 计划。
每个发现 agent 写 Deploy risk:(什么会在什么时候 break——在 deploy 时 / 在 prod 中 / 随着时间推移)和 Rollback:(有没有 rollback 计划)。这将 review 变成 deploy 讨论的 checklist,而不是抽象的"可能出问题"。
2026 年"社会效益"最高的 agent,因为很大一部分 PR 都是 AI 辅助的。我也用这个 review 来跑其他 devs 的代码——可能部分或完全 vibe-coded,没有这个过滤器很多垃圾会进入 master。另外,它 catch 我自己因疲劳而出错的代码:当你整天做一个任务时,像 try/except: pass、忘记的 print、多余的 helper 或注释掉的块"我待会儿删"——你就是注意不到它们。Agent 是一次新鲜的视角。
它的职责是把代码拉回到最小必要解。它 catch 的内容:
settings.XXXXX(配置中不存在的 key)。通过 grep 项目验证。class Helper/Manager/Factory、单一实现的 ABC/Protocol、单一 type-param 的 generic、一次性的 decorator、一个 import 就够的地方用 DI。x = get_x() 返回 X 后紧接着 if x is not None、无缘无故的 try/except: pass、对内部数据用 isinstance、验证已经被 serializer 验证过的输入。# increment counter 在 counter += 1 前。if/else。flatten/chunked/lru_cache。calc_total 时用 calculate_total_price_with_discount)、日志中其他任何地方都没有的 emoji、"Successfully completed..."日志、过于礼貌的错误消息。Agent 的主要规则:"删除它会让代码变差吗?如果不会——就是噪声。"
可选——只在 docker-stack 启动时工作。从不运行完整 suite(那得好几小时),而是做三件事:找到与 diff 相关的测试、并行运行它们、计算变更行的 differential coverage。
相关性通过四种方式同时计算:按目录结构(app/module.py → app/tests/test_module.py)、按 imports(grep from .module import)、按测试中的函数/类名(grep 符号)、以及 diff 中的测试文件本身。然后——dedup,限制 20 个文件,pytest --lf -n auto -x(先跑上次失败的,按核心并行,遇到第一个失败就停)。
Differential coverage——最有用的部分。我们取 coverage json,将 executed_lines 与 diff 中的变更行比较,计算百分比。如果低于 50%——High。这比整体项目覆盖率诚实多了:可能整体是 80%,但实际新代码零测试。
如果 docker 不可用——状态 SKIPPED,agent 仍然返回测试列表供手动运行。这不是错误,是正常模式。
连接一切的指挥。这里有个 Claude Code SDK 的架构限制:subagent 不能 spawn 其他 subagents。所以 orchestrator 不是单独的 subagent,它是一个 playbook,主 agent 在其主 context 中读取并遵循。
五个步骤:
git diff --merge-base origin/master HEAD,stats,文件列表,commits。保存到 /tmp。git blame -L,构建 file:line → author + sha。这个块然后进入最终表——每个发现旁边显示是谁的代码。对 PR review 讨论很有用。(file, line, category)去重(同一 file+lines 在多个 agents 中 = 表中一行,"Aspect"列通过斜杠列出所有 agents)、按 Blocker → High → Medium → Low 优先级、格式化最终 markdown 表,结论为 READY TO MERGE / NEEDS ATTENTION / NEEDS WORK。我踩过的坑:
/tmp 路径。Subagents 一直挂在 cat /tmp/review_diff.patch 上——原来某些运行时它们的 /tmp 和主 agent 的 /tmp 不同。解决方案:将 diff 直接粘贴到 prompt 中,放在 === DIFF === ... === END DIFF === 之间。每个 artifact ~20 KB 限制。server/path/file.py:LINE,不是 /Users/...)——否则 IDE 链接无法点击,与同事共享也会坏。Verify by:——一个具体命令。防止 agent 写抽象的"这里可能有问题"。(file, line_range, category)去重——否则一个 bug 被三个 agent 报告 3×,review 变得无法阅读。所有 agents 放在 .claude/agents/.md,skills 放在 .claude/commands/.md。这些只是带 frontmatter(name、description、model、tools)和正文中 instructions 的 markdown 文件。Claude Code 自动加载它们。全局安装——同样内容放在 ~/.claude/agents/ 和 ~/.claude/commands/。
Minimal subagent 模板:
---
name: my-reviewer
description: Brief description for the main agent — when to call
model: sonnet
tools: [Read, Grep, Glob, Bash]
What to do, what to check, response format.
实践踩坑心得:
Read、Bash、Grep、Glob、Agent)。read_file/bash 不工作。sonnet / opus / haiku / inherit。Orchestrator 用 inherit 最好——它在当前 session 的模型上运行。Agent 工具——它们不能 spawn 其他 subagents,如果尝试,runtime 会以模糊错误 crash。=== AGENT: name === ... === END: name ===),FINDINGS 包含 ID、Severity、Confidence、Location、Fix、Verify by。自由格式输出 → 合成变得一团糟。Skills(slash-commands)——更简单。Markdown 描述如何处理来自 $ARGUMENTS 的用户输入。我做的好用 home-skills:
/diff——分支相对于 master 的变更摘要(文件、commits、影响的函数、migrations)。PR 前 handy。/lint——make format(ruff + auto-fix)的 wrapper。/find-usage ——搜索符号的所有用法(定义 → imports → 调用 → tests → templates)。/debug ——traceback 分析:最后一帧、在正确行读源码、修复建议。/logs [search] [--lines=N] [--level=ERROR]——在 docker-container 内查看结构化日志。/orm ——在 docker 内运行 Django ORM 或原始 SQL(带 auto-LIMIT,DELETE/UPDATE 时请求确认)。/new-test [name] ——按项目约定生成 pytest 文件并通过 docker 运行。/make [args] ——Makefile 的代理,带分类引用。Skills 的要点——它们应该 薄。不是"通用 devops agent",而是"一个命令的 wrapper 加几个 if-else"。Skill 自由度越小,结果越可预测。
除了代码 review agents,还有一个选项可以让 bots 从语言理解层面指出开发者的不足——写的代码可以通过所有检查但不是最理想的(没有这种事)。Bot 也可以建议你可以改进的地方、观察你的成长动态并给予建议。
步骤:
!图片
已经存在的问题:agents 写代码成本相当高 + agents 无法处理 legacy 代码 + 项目 devs 和 analysts 对其成长很重要。新的方法正在出现,有潜力解决这些问题,但我们仍然需要在这个世界中生存,应该尽可能高效地工作,以便存活更长时间、成长快过新的 AI 模型 :)
!图片
最重要的事——Bender 是我们的兄弟,不进则退!