我们用 Claude 删除了 5,600 行代码(并发现了 1 个 Bug)

发布: (2026年1月14日 GMT+8 09:14)
10 分钟阅读
原文: Dev.to

Source: Dev.to

我构建的过度设计系统

我需要管理提供商(Google Cloud、AWS、Postgres)及其服务(BigQuery、Firestore、DynamoDB)。看起来很简单。

但我却做出了下面这个结构:

位置存储内容
seeds/sources_seed.go包含服务数组的源实体
seeds/templates_seed.go包含模式的 ConnectionTemplate 实体
seeds/constants.go硬编码的 UUID
frontend/components/connections/index.tsUUID → 组件映射
frontend/components/connections/*.tsx硬编码的 selectedServices 数组
frontend/lib/services/google-cloud-capabilities.ts每个服务的 OAuth 范围
数据库(Firestore)SourcesConnectionTemplates 集合

八个以上文件以不同方式定义相同信息。

  • 想要添加一个新提供商?需要修改 8 个文件。
  • 想要在已有提供商中添加服务?需要修改 6 个文件。
  • 想要弄清楚它们是如何关联的?祝你好运。

我当时告诉自己这很“灵活”“可扩展”。

领悟

然后我真的思考了一下:

  • 没有任何动态性。 OAuth 范围在 OAuth 应用层面被锁定。你无法为每个连接授予不同的范围——它们已经写入 OAuth 同意屏幕。
  • 没有部分服务访问的使用场景。 “这个 Google Cloud 连接拥有 BigQuery 但没有 Firestore”——这种情况什么时候会出现?连接仅仅是凭证。如果这些凭证无法访问 BigQuery,API 会返回 401。就这么简单。
  • 我在为从未使用的灵活性维护复杂性。

“高级”架构在解决一个根本不存在的问题。

替代方案:20 行

我还没有任何用户,没有向后兼容性的顾虑,也没有数据需要迁移。

于是我没有去重构,而是问自己:如果我直接把所有东西都删掉,改用一个配置文件会怎样?

// frontend/lib/providers.ts
export const PROVIDERS = {
  'google-cloud': {
    name: 'Google Cloud Platform',
    auth: 'oauth2',
    services: ['bigquery', 'firestore', 'gcs', 'pubsub'],
  },
  'aws': {
    name: 'Amazon Web Services',
    auth: 'iam',
    services: ['dynamodb'],
  },
  'postgres': {
    name: 'PostgreSQL',
    auth: 'database',
    services: ['postgres'],
  },
} as const

export type ProviderId = keyof typeof PROVIDERS

就这么简单——大约 20 行代码取代了数据库表、种子数据、UUID、能力文件以及注册表。

前后对比:连接模型

之前: 6 个字段,UUID 查询,冗余数据

// Before
Connection = {
  id: 'abc123',
  sourceId: 'c7b3d8e9-5f2a-4b1c-9d6e-8a3b5c7d9e1f',   // UUID 查询
  templateId: 'template-google-cloud-oauth2',        // 另一个 UUID
  services: ['bigquery', 'firestore'],               // 冗余
  connectionConfig: {
    selectedServices: ['bigquery', 'firestore'],     // 重复
    projectId: 'my-project',
  },
  credentials: { /* … */ }
}

之后: 4 个字段,字符串 ID,无冗余

// After
Connection = {
  id: 'abc123',
  providerId: 'google-cloud',           // 直接使用 PROVIDERS 中的键
  config: { projectId: 'my-project' }, // 供应商特定的配置
  credentials: { /* … */ }
}

Claude 如何实现这一点

这并不是“让 Claude 写点代码”。这是一场 AI 辅助的架构手术

1. 绘制影响范围

我向 Claude 提供了代码库的上下文,并让它找出所有引用旧系统的文件。它识别出了:

  • 所有旧类型的导入
  • 所有 UUID 常量的使用
  • 使用 sourceIdtemplateId 的前端组件
  • 需要更新的测试文件
  • 为避免破坏中间状态而必须遵循的操作顺序

2. 系统化执行

194 个文件手动修改既繁琐,又难以 正确 完成。Claude 按部就班地完成了这些工作:

后端 (Go)

  • connections/model.go – 删除 servicestemplateId;添加 providerId
  • connections/service.go – 更新创建/校验逻辑
  • connections/handler.go – 更新 API 请求/响应
  • connections/dao.go – 更新 Firestore 查询
  • router.go – 删除 /v1/sources/* 路由
  • 删除整个 sources/ 包(9 个文件)
  • 删除整个 connectiontemplate/ 包(10 个文件)
  • 删除 seeds/sources_seed.gotemplates_seed.go

前端 (TypeScript)

  • lib/providers.ts – 新的静态配置(20 行)
  • 删除 hooks/use-source.ts
  • 删除 hooks/use-connection-template.ts
  • 删除 services/source-service.ts
  • 删除 services/google-cloud-capabilities.ts
  • 更新 40 多个使用旧类型的组件文件

3. 同步更新测试

关键点: 我们没有删除测试,而是对其进行了更新。

  • 删除 sources/ 包时,也同步删除了对应的测试。
  • 简化连接模型时,更新了连接相关的测试。

整个重构过程中,测试套件始终保持绿色。

为什么只有一个 bug?

在修改了 194 个文件后,我们在端到端测试中恰好发现了 一个 bug。这不是运气——而是以下因素的结果:

  1. 全面的测试覆盖。 测试能够立即捕获回归。当我更改连接模型时,测试准确指出了哪些处理程序和服务需要更新。
  2. 在有测试的情况下进行重构,而不是事后重构。 每一次更改都伴随相应的测试更新,确保测试套件在每一步都保持可靠。

结论

过度工程化会让你陷入维护噩梦。通过剔除不必要的层级并让 AI 帮助映射影响,我将一个 5,600 行的系统压缩到整洁的 20 行配置——节省了时间、降低了复杂度,并避免了未来的 bug。

同一次提交中的测试更新

测试不是事后才考虑的。

3. AI 帮助你系统化

Claude 不会忘记在代码库的某个偏远角落更新文件。它不会在第 80 个文件后疲惫而开始出错。

4. 你拥有清晰的架构愿景

我在动手写代码之前写了一份详细的计划文档。目标状态明确无误:一个配置文件,基于字符串的提供者 ID,连接上没有服务数组。

我学到的

复杂性是可以选择的

我构建了这个复杂的系统。没有人强迫我为静态数据创建基于 UUID 的查找和数据库种子。我这么做是因为觉得“合适”。
有时候,合适的方案只需要一个 20 行的配置文件。

AI 最适合用于架构,而不仅仅是自动补全

价值不在于“Claude 写代码更快”。价值在于:

  • Claude 帮助识别了旧系统的所有触角
  • Claude 在 194 个文件之间保持了上下文
  • Claude 采用系统化的方式,而我会感到疲惫

经过充分测试的代码让删除无所畏惧

我可以批量删除代码,因为我信任我的测试。每一次删除都有验证。没有“我觉得这安全”的说法——要么测试通过,要么不通过。

没有用户 = 没有借口

目前还没有用户,这意味着我没有理由保留复杂性。没有向后兼容的需求。没有迁移脚本。只需删除并继续前进。

如果你处于早期阶段并且背负技术债务,现在是修复它的最便宜时机。

新开发者体验

添加新提供者

  1. PROVIDERS 配置中添加条目(1 行)
  2. 创建 connection‑form 组件
  3. 创建 service‑config 组件
  4. 创建后端处理程序

为现有提供者添加服务

  1. PROVIDERS[providerId].services 数组中添加(1 行)
  2. 创建 service‑config 组件
  3. 创建后端处理程序

无种子数据。无迁移。无 UUID。无模板实体。

亲自尝试

如果你正盯着一个感觉比实际需要的更臃肿的系统:

  • 询问它在解决什么问题。 这个问题是真实的还是假设的?
  • 检查是否真的有动态部分。 如果“灵活”的部件从不灵活,它们只是增加了复杂度。
  • 如果你有完善的测试,就相信它们。 测试会捕捉你的错误。
  • 使用 AI 来绘制影响范围。 它比你更擅长找出所有引用。

有时答案就是大规模删除。

你是否曾经删除过自己搭建的系统,因为意识到它被过度设计了?是什么帮助你做出这个决定的?

Back to Blog

相关文章

阅读更多 »