跳到主要内容

使用Spec重构

并非所有Spec都是功能。学习如何使用 LeanSpec 进行重构和架构变更,其中文档至关重要。

时间:15 分钟
结果:完成带有清晰文档的重构

前置条件

立即尝试

使用准备好重构的项目快速上手:

npx lean-spec init --example api-refactor
cd api-refactor
npm install
npm start

然后向 AI 工具发送提示:

在 IDE 中使用 AI 编码助手(GitHub Copilot、Claude、Cursor 等):

  1. 在 IDE 中打开项目
  2. 打开 AI 助手聊天面板
  3. 发送提示:"帮我重构这个应用,提取一个可重用的 API 客户端"

想了解底层发生了什么? 下面的章节将详细介绍每个步骤。

场景

您的 API 客户端分散在整个代码库中。每个组件都直接导入 Axios,这使得难以:

  • 添加请求/响应拦截器
  • 切换 HTTP 库
  • 在测试中模拟 API 调用
  • 跟踪 API 使用情况

您需要提取一个集中的 ApiClient 类。这是重构,而不是功能——没有面向用户的变化,但架构得到改善。

起点:

your-project/
├── src/
│ ├── components/
│ │ ├── UserProfile.tsx # 直接导入 axios
│ │ └── Dashboard.tsx # 直接导入 axios
│ └── services/
│ ├── auth.service.ts # 直接导入 axios
│ └── data.service.ts # 直接导入 axios
└── specs/
└── (您现有的Spec)

创建技术Spec

询问您的 AI:

创建一个用于提取集中式 API 客户端的Spec。
将所有 axios 调用移动到单个 ApiClient 类,包括:
- 请求/响应拦截器
- 错误处理
- Token管理
- 类型安全方法

标记为 "refactoring" 和 "architecture"。
设置优先级为高。

AI 创建 specs/024-centralize-api-client/README.md,包含:

---
status: planned
priority: high
tags: ['refactoring', 'architecture']
---

# 集中 API 客户端

## 概述
将分散的 Axios 调用提取到单个 ApiClient 类中。

当前痛点:整个代码库中有 47 个直接 axios 导入,没有一致的
错误处理,无法全局添加认证拦截器,测试模拟 axios
而不是我们的 API 层。

目标:具有集中错误处理和拦截器的可维护 HTTP 层。
单一导入点,易于切换 HTTP 库,更好的可测试性。

## 设计
新的 ApiClient 类,具有类型化的 get/post 方法和用于
认证Token注入和错误处理的请求/响应拦截器。

(...图表或代码片段...)

迁移策略:
1. 创建带有基本方法的 ApiClient
2. 添加认证拦截器
3. 首先替换服务层中的 axios(4 个文件)
4. 替换组件中的 axios(15 个文件)
5. 更新测试并移除 axios 依赖

非目标:不更改 API 端点或响应数据结构。

## 计划
- [ ] 创建带有类型化方法的 src/lib/api-client.ts
- [ ] 实现请求/响应拦截器
- [ ] 迁移服务层
- [ ] 迁移组件层
- [ ] 更新测试模拟
- [ ] 移除直接的 axios 导入

## 测试
- [ ] 所有现有测试通过
- [ ] ApiClient 测试覆盖错误处理
- [ ] 认证Token正确注入到请求中

这里有什么不同? 这是一个技术Spec:

  • 专注于架构,而不是用户功能
  • 包括迁移策略(顺序很重要)
  • 明确的非目标防止范围蔓延
  • 详细计划,因为重构有风险

实施

询问您的 AI:

实施Spec 024。逐步进行:
1. 首先创建 ApiClient
2. 在迁移之前向我展示实现
3. 然后迁移一个服务作为测试
4. 等待我的批准后再继续

AI 逐步进行:

步骤 1: 创建 src/lib/api-client.ts

export class ApiClient {
private client: AxiosInstance;

constructor() {
this.client = axios.create({ baseURL: process.env.API_BASE_URL });
// 认证拦截器向请求添加Token
}

async get<T>(path: string): Promise<T> { /* ... */ }
async post<T>(path: string, data: any): Promise<T> { /* ... */ }
}

步骤 2: 迁移一个服务作为证明

// src/services/auth.service.ts
- import axios from 'axios';
+ import { apiClient } from '@/lib/api-client';

- const { data } = await axios.post('/auth/login', credentials);
+ const data = await apiClient.post('/auth/login', credentials);

您审查、测试并批准:"看起来不错。继续处理其余服务和组件。"

步骤 3: AI 迁移其余文件,更新测试,移除 axios 导入

审查和完成

运行测试套件:

npm test

所有测试通过?在开发中验证:

npm run dev
# 点击查看,检查网络选项卡

一切正常。完成Spec:

lean-spec update 024 --status complete

Spec现在记录了您进行此更改的 原因 以及 如何 完成的。未来的开发人员可以阅读它以了解架构决策。

刚才发生了什么

您记录并执行了一个复杂的重构:

没有 LeanSpec:

  • 重构理由在 PR 描述中丢失
  • 实现细节在 git 历史中(难以找到)
  • 未来的开发人员会问"我们为什么这样做?"
  • 范围蔓延:"在重构时,让我们也..."

有了 LeanSpec:

  • 在编码之前清楚地捕获理由
  • 迁移策略已记录
  • AI 逐步遵循计划
  • 非目标防止功能蔓延
  • 完成的Spec = 永久记录

Spec成为 架构文档,向未来的维护者解释过去的决策。

何时为重构编写Spec

编写Spec时:

  • 重构影响 >5 个文件
  • 架构决策需要解释
  • 迁移顺序很重要
  • 有破坏现有行为的风险

跳过Spec时:

  • 重命名变量/文件
  • 提取单个函数
  • 修复代码风格
  • 明显的改进

重构Spec检查清单:

  • "为什么这很重要"部分解释痛点
  • 设计显示前后结构
  • 迁移策略是明确的
  • 非目标防止范围蔓延
  • 测试标准确保没有回归

关键模式

技术Spec包括:

  • 理由:我们为什么这样做(痛点)
  • 设计:架构变更(图表有帮助)
  • 迁移:变更顺序(对重构至关重要)
  • 非目标:我们不做什么(范围控制)
  • 测试:如何验证没有破坏任何东西

AI 协助最适合:

  • 逐步说明("做 X,等待批准")
  • 清晰的迁移顺序
  • 每个阶段后测试方法
  • 明确的文件路径

下一步

尝试重构代码库中的某些内容:

  • 选择一个分散的关注点(日志记录、错误、配置)
  • 创建带有迁移策略的Spec
  • 让 AI 逐步实施
  • 完成并记录决策

了解更多:

高级:

有问题? 查看 FAQ开始讨论