Superpowers Day 2:我学会了 TDD 和 Git Worktrees,这是我的实践记录

0 阅读14分钟

Superpowers Day 2:我学会了 TDD 和 Git Worktrees,这是我的实践记录

4 周学习 Superpowers 系列文章 - Day 2

🎯 写在前面

Day 1 中,我用 subagent-driven-development 模式完成了第一个项目(极简计算器)。虽然是 13 个任务全部自动完成,但我其实没有真正理解 TDD 的每个步骤,也没看到 Git Worktrees 是如何创建的。

今天,我决定用 手动分步执行模式 重做一个项目,深入观察 TDD 的 RED-GREEN-REFACTOR 循环,以及 Git Worktrees 的创建和管理过程。


📚 项目:Python 字符串处理工具库

技术选型

  • 语言:Python 3.x
  • 测试框架:pytest
  • 代码规范:PEP 8
  • 类型注解:Optional, Dict, bool, str

功能需求

  1. reverse_string:反转字符串
  2. count_chars:统计字符出现次数
  3. is_palindrome:判断回文
  4. remove_duplicates:去除重复字符
  5. convert_case:大小写转换

边界情况

  • 空字符串处理
  • None 输入处理
  • 特殊字符支持
  • Unicode 支持

🎬 执行流程:手动模式 vs 自动模式

Day 1 vs Day 2 对比

维度Day 1(自动模式)Day 2(手动模式)
执行方式subagent-driven手动分步执行
控制程度🟢 低🔴 高
TDD 观察❌ 看不到 RED-GREEN-REFACTOR✅ 完整看到每个步骤
Git Worktrees✅ 自动创建,看不见✅ 看到创建过程
学习效果📚 中等📚 优秀
开发时间42 分钟30 分钟
参与感🟢 弱🔴 强

🚀 阶段 1:Brainstorming(需求澄清)

Claude 问我什么?

好的!在开始之前,我需要了解一些细节:

1. 这个工具库的用途是什么?个人工具 / 开源项目 / 商业项目?
2. 每个函数的接口设计?
3. 需要处理哪些边界情况?
4. 需要类型注解吗?
5. 需要文档字符串(docstring)吗?

我的回答

1. 个人学习项目
2. 接口设计:
   - reverse_string(s: str) -> str
   - count_chars(s: str) -> Dict[str, int]
   - is_palindrome(s: str) -> bool
   - remove_duplicates(s: str) -> str
   - convert_case(s: str, mode: str) -> str
3. 边界情况:空字符串、None 输入、特殊字符、Unicode
4. 需要类型注解
5. 需要文档字符串

自动生成设计文档

文件: docs/superpowers/specs/2026-03-21-python-string-tools-design.md

# Python 字符串处理工具库设计文档

**日期**: 2026-03-21
**状态**: 已批准

## 1. 概述
构建一个 Python 字符串处理工具库,提供常用的字符串操作函数。

## 2. 功能需求

### 2.1 核心功能
- reverse_string(s) - 反转字符串
- count_chars(s) - 统计字符出现次数
- is_palindrome(s) - 判断回文
- remove_duplicates(s) - 去除重复字符
- convert_case(s, mode) - 大小写转换

### 2.2 接口设计
```python
def reverse_string(s: str) -> Optional[str]
def count_chars(s: str) -> Optional[Dict[str, int]]
def is_palindrome(s: str) -> bool
def remove_duplicates(s: str) -> Optional[str]
def convert_case(s: str, mode: str) -> Optional[str]

3. 边界情况

3.1 None 输入

所有函数返回 None

3.2 空字符串

reverse_string("") → "" is_palindrome("") → True

3.3 特殊字符

正确处理 Unicode 字符

4. 验收标准

4.1 功能验收

  • 所有函数正常工作
  • 有完整的测试覆盖
  • 有类型注解
  • 有文档字符串

4.2 具体测试用例

用例输入预期结果
反转字符串"hello""olleh"
统计字符"hello"{"h": 1, "e": 1, "l": 2, "o": 1}
判断回文"racecar"True
去除重复"hello""helo"
大小写转换("hello", "upper")"HELLO"
None 输入NoneNone

**自动生成设计文档的优势:**
-  需求更清晰
-  作为后续参考
-  零成本生成

---

## 🔍 阶段 2:TDD 实战(重点观察)

### Task 2:实现字符串反转

#### 【RED】编写失败的测试

**我看到的:**

【TDD - RED】编写失败的测试...

创建文件:tests/test_reverse.py

添加测试用例: ✓ test_reverse_normal_string() - "hello" → "olleh" ✓ test_reverse_empty_string() - "" → "" ✓ test_reverse_none_input() - None → None

测试代码:

import pytest
from string_tools import reverse_string

def test_reverse_normal_string():
    assert reverse_string("hello") == "olleh"

def test_reverse_empty_string():
    assert reverse_string("") == ""

def test_reverse_none_input():
    assert reverse_string(None) is None

运行测试... ❌ 失败(预期!)


**我的感受:**
- 测试清晰地描述了功能需求
- 写测试时,我更理解了边界情况
- 测试失败是正常的,这是 TDD 的第一步

---

#### 【GREEN】实现最小代码

**我看到的:**

【TDD - GREEN】实现最小代码...

创建文件:src/string_tools.py

实现函数:

from typing import Optional

def reverse_string(s: str) -> Optional[str]:
    """反转字符串"""
    if s is None:
        return None
    return s[::-1]

运行测试... ✅ 通过!

实现特点: ✓ 最小代码,只解决问题(YAGNI) ✓ 使用 Python 切片,简洁高效 ✓ 处理 None 边界情况


**我的感受:**
- 只写能通过测试的最小代码
- 不过度设计(YAGNI 原则)
- 快速验证思路

---

#### 【REFACTOR】优化代码

**我看到的:**

【TDD - REFACTOR】优化代码...

代码审查检查: ✓ 符合规格要求 ✓ 有类型注解 ✓ 有文档字符串 ✓ 代码清晰易读 ✓ 遵循 PEP 8

优化内容: ✓ 无需优化(代码已经很好)

【代码审查】通过!✓


**我的感受:**
- 在测试保护下,重构更安全
- 代码质量有保障

---

### Task 3:实现统计字符

#### 【RED】编写失败的测试

【TDD - RED】编写失败的测试...

添加测试用例: ✓ test_count_chars_normal() - "hello" → {"h": 1, "e": 1, "l": 2, "o": 1} ✓ test_count_chars_empty() - "" → {} ✓ test_count_chars_none() - None → None

测试代码:

def test_count_chars_normal():
    assert count_chars("hello") == {"h": 1, "e": 1, "l": 2, "o": 1}

def test_count_chars_empty():
    assert count_chars("") == {}

def test_count_chars_none():
    assert count_chars(None) is None

运行测试... ❌ 失败(预期!)


---

#### 【GREEN】实现最小代码

【TDD - GREEN】实现最小代码...

实现函数:

from typing import Optional, Dict

def count_chars(s: str) -> Optional[Dict[str, int]]:
    """统计字符出现次数"""
    if s is None:
        return None
    result = {}
    for char in s:
        result[char] = result.get(char, 0) + 1
    return result

运行测试... ✅ 通过!

实现特点: ✓ 使用字典统计 ✓ 处理 None 边界情况


---

#### 【REFACTOR】优化代码

【TDD - REFACTOR】优化代码...

优化建议:可以使用字典推导式

优化后的代码:

from collections import Counter

def count_chars(s: str) -> Optional[Dict[str, int]]:
    """统计字符出现次数"""
    if s is None:
        return None
    return dict(Counter(s))

运行测试... ✅ 仍然通过!

【代码审查】通过!✓


**我的感受:**
- 在测试保护下重构,非常安全
- 重构后代码更简洁

---

## 🌳 阶段 3:Git Worktrees 实战

### 创建过程(手动模式)

**我看到的:**

【Step 1】创建新的 Git Worktree...

正在执行:git checkout -b feature/02-reverse ✓ 分支已创建:feature/02-reverse

正在执行:git worktree add ../worktree-02 feature/02-reverse ✓ Worktree 已创建:/path/to/worktree-02

【Step 2】进入 worktree 工作区... 当前工作区:/path/to/worktree-02


**目录结构:**

```day2-python-string-tools/
├── .git/                    # 共享的 Git 数据库
├── README.md
├── requirements.txt
├── src/
│   └── string_tools.py      # 主分支(初始为空)
├── tests/
│   └── test_string_tools.py # 主分支(初始为空)
├── worktree-01/              # Task 1 工作区
├── worktree-02/              # Task 2 工作区(独立!)
│   ├── src/
│   │   └── string_tools.py  # 独立版本
│   └── tests/
│       └── test_reverse.py  # 独立测试
└── ...

关键理解:

  • ✅ 每个任务在独立的 worktree 中工作
  • ✅ 互不干扰,可以并行开发
  • ✅ 容易回滚和修改
  • ✅ 测试隔离

Git Worktrees 命令

查看所有 worktrees:

$ git worktree list

/path/to/day2-python-string-tools   8ea71da [master]
/path/to/worktree-01               abc1234 [feature/01-init]
/path/to/worktree-02               def5678 [feature/02-reverse]
/path/to/worktree-03               fedc098 [feature/03-count]
...

创建新的 worktree:

# 创建新分支
git checkout -b feature/03-count

# 创建 worktree
git worktree add ../worktree-03 feature/03-count

# 进入 worktree
cd ../worktree-03

# 工作...
git add .
git commit -m "feat: add count_chars function"

删除 worktree(任务完成后):

# 删除 worktree
git worktree remove ../worktree-02

# 删除分支
git branch -d feature/02-reverse

Git Worktrees 的优势

1. 上下文隔离
worktree-02/    只有 reverse_string 的代码
worktree-03/    只有 count_chars 的代码
worktree-04/    只有 is_palindrome 的代码

→ 每个任务只看到自己需要的东西
→ 不会被其他代码干扰
→ 容易理解和审查
2. 并行开发
子代理 1 在 worktree-02/ 做任务 2
子代理 2 在 worktree-03/ 做任务 3
子代理 3 在 worktree-04/ 做任务 4

→ 可以同时进行多个任务
→ 互不干扰
→ 提高效率
3. 独立测试
worktree-02/
  pytest test_reverse.py          # 只运行任务 2 的测试

worktree-03/
  pytest test_count.py            # 只运行任务 3 的测试

→ 测试隔离,不会互相影响
→ 容易定位问题
4. 轻松回滚
如果任务 3 做错了:

❌ 传统方式:
   整个项目都被污染
   回滚很麻烦

✅ Git Worktrees:
   只删除 worktree-03/
   重新创建一个 worktree-03/
   重新做任务 3

→ 干净、简单、快速

🎯 阶段 4:所有任务完成

项目结构(合并前)

day2-python-string-tools/
├── .git/
├── README.md
├── requirements.txt
├── src/
│   └── string_tools.py      # 主分支(初始为空)
├── tests/
│   └── test_string_tools.py # 主分支(初始为空)
├── worktree-01/              # Task 1:项目初始化
├── worktree-02/              # Task 2:reverse_string
├── worktree-03/              # Task 3:count_chars
├── worktree-04/              # Task 4:is_palindrome
├── worktree-05/              # Task 5:remove_duplicates
├── worktree-06/              # Task 6:convert_case
└── worktree-07/              # Task 7:集成测试

触发 finishing-a-development-branch

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 所有任务完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

项目统计:
- 7 个任务完成
- 所有测试通过
- 耗时约 30 分钟
- 零 Bug

下一步操作,请选择:

【1】Merge - 合并所有分支到主分支
【2】Pull Request - 创建 PR 供审查
【3】Keep - 保留所有分支供后续工作
【4】Discard - 丢弃所有分支

你想怎么做?

我选择:Merge

【Step 1】切换回主分支
  ✓ git checkout master

【Step 2】合并所有分支
  ✓ git merge feature/01-init
  ✓ git merge feature/02-reverse
  ✓ git merge feature/03-count
  ✓ git merge feature/04-palindrome
  ✓ git merge feature/05-dedup
  ✓ git merge feature/06-convert
  ✓ git merge feature/07-integration

【Step 3】清理 worktree
  ✓ 删除 worktree-01 到 worktree-07

【Step 4】删除临时分支
  ✓ 删除 feature/01-init 到 feature/07-integration

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ 合并完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

最终项目结构

day2-python-string-tools/
├── .git/
├── README.md               ✅ 完整文档
├── requirements.txt        ✅ pytest
├── src/
│   ├── __init__.py         ✅
│   └── string_tools.py     ✅ 5 个函数
└── tests/
    ├── __init__.py         ✅
    └── test_string_tools.py ✅ 15+ 测试用例

💡 核心收获

1. TDD 的真正威力

RED 阶段:测试即需求
# 写测试时,我更理解了需求
def test_reverse_normal_string():
    assert reverse_string("hello") == "olleh"

# 这个测试明确告诉我:
# - 函数名是 reverse_string
# - 输入是 "hello"
# - 输出应该是 "olleh"
# - 返回类型是 str

我的感受:

  • ✅ 测试就是需求的精确描述
  • ✅ 写测试时更理解功能
  • ✅ 测试可以作为文档使用

GREEN 阶段:YAGNI 原则
# 只写最小能通过的代码
def reverse_string(s: str) -> Optional[str]:
    if s is None:
        return None
    return s[::-1]

# 没有过度设计:
# ❌ 没有添加不必要的功能
# ❌ 没有过度抽象
# ❌ 没有过度优化

我的感受:

  • ✅ 只解决当前问题
  • ✅ 代码更简洁
  • ✅ 开发更快

REFACTOR 阶段:在测试保护下优化
# 原始代码
def count_chars(s: str) -> Optional[Dict[str, int]]:
    if s is None:
        return None
    result = {}
    for char in s:
        result[char] = result.get(char, 0) + 1
    return result

# 重构后
def count_chars(s: str) -> Optional[Dict[str, int]]:
    if s is None:
        return None
    return dict(Counter(s))

# 有测试保护,重构很安全!

我的感受:

  • ✅ 重构更安全
  • ✅ 代码质量更高
  • ✅ 不破坏已有功能

2. Git Worktrees 的价值

对比传统 Git

传统 Git:

day2-python-string-tools/
├── src/
│   └── string_tools.py      # 所有代码混在一起
└── tests/
    └── test_string_tools.py # 所有测试混在一起

# 修改时:
git checkout feature/02-reverse
[做任务 2...]
# 突然需要做任务 3
git stash          # 保存进度
git checkout feature/03-count
[做任务 3...]
git stash pop       # 恢复进度

问题:

  • ❌ 频繁切换分支,容易混乱
  • ❌ 需要使用 stash
  • ❌ 容易忘记保存进度

Git Worktrees:

day2-python-string-tools/
├── worktree-01/              # 任务 1 独立
├── worktree-02/              # 任务 2 独立
├── worktree-03/              # 任务 3 独立

# 同时工作:
# 终端 1
cd worktree-02/
[做任务 2...]

# 终端 2
cd worktree-03/
[做任务 3...]

# 互不干扰!

优势:

  • ✅ 不需要切换分支
  • ✅ 不需要 stash
  • ✅ 可以同时工作

3. 手动模式 vs 自动模式

Day 1(自动模式)

体验:

你说:"go"
Claude:[自动执行所有任务]
你:[等待 42 分钟]
Claude:🎉 完成!

你看不到:
- ❌ TDD 的 RED-GREEN-REFACTOR
- ❌ Git Worktrees 的创建
- ❌ 每个步骤的细节

适合:

  • 大型项目
  • 有清晰规格
  • 信任 AI
  • 想快速完成

Day 2(手动模式)

体验:

你说:"开始 Task 2"
Claude:[一步步展示 TDD]
      【RED】写测试...
      【GREEN】写代码...
      【REFACTOR】优化...

你:[每步都看到]
Claude:[展示 Git Worktree 创建]
      git worktree add ../worktree-02 feature/02-reverse

你:[完全理解过程]

适合:

  • 学习理解
  • 需要控制
  • 小型项目
  • 想边看边学

我的选择:

  • Day 1:自动模式(快速完成)
  • Day 2:手动模式(深入理解)
  • 以后会根据需求灵活选择

📊 项目成果

最终代码

src/string_tools.py:

"""字符串处理工具库"""

from typing import Optional, Dict
from collections import Counter


def reverse_string(s: str) -> Optional[str]:
    """反转字符串

    Args:
        s: 要反转的字符串

    Returns:
        反转后的字符串,如果输入为 None 则返回 None
    """
    if s is None:
        return None
    return s[::-1]


def count_chars(s: str) -> Optional[Dict[str, int]]:
    """统计字符出现次数

    Args:
        s: 要统计的字符串

    Returns:
        字符出现次数的字典,如果输入为 None 则返回 None
    """
    if s is None:
        return None
    return dict(Counter(s))


def is_palindrome(s: str) -> bool:
    """判断是否为回文

    Args:
        s: 要判断的字符串

    Returns:
        如果是回文返回 True,否则返回 False
        空字符串视为回文
    """
    if not s:
        return True
    return s == s[::-1]


def remove_duplicates(s: str) -> Optional[str]:
    """去除重复字符

    Args:
        s: 要处理的字符串

    Returns:
        去除重复字符后的字符串,如果输入为 None 则返回 None
    """
    if s is None:
        return None
    return "".join(dict.fromkeys(s))


def convert_case(s: str, mode: str) -> Optional[str]:
    """大小写转换

    Args:
        s: 要转换的字符串
        mode: 转换模式(upper/lower/title)

    Returns:
        转换后的字符串,如果输入为 None 则返回 None

    Raises:
        ValueError: 如果 mode 不是 upper/lower/title
    """
    if s is None:
        return None
    if mode == "upper":
        return s.upper()
    elif mode == "lower":
        return s.lower()
    elif mode == "title":
        return s.title()
    else:
        raise ValueError(f"Invalid mode: {mode}")

tests/test_string_tools.py:

"""字符串处理工具库测试"""

import pytest
from string_tools import (
    reverse_string,
    count_chars,
    is_palindrome,
    remove_duplicates,
    convert_case,
)


def test_reverse_normal_string():
    assert reverse_string("hello") == "olleh"


def test_reverse_empty_string():
    assert reverse_string("") == ""


def test_reverse_none_input():
    assert reverse_string(None) is None


def test_count_chars_normal():
    assert count_chars("hello") == {"h": 1, "e": 1, "l": 2, "o": 1}


def test_count_chars_empty():
    assert count_chars("") == {}


def test_count_chars_none():
    assert count_chars(None) is None


def test_is_palindrome_true():
    assert is_palindrome("racecar") is True


def test_is_palindrome_false():
    assert is_palindrome("hello") is False


def test_is_palindrome_empty():
    assert is_palindrome("") is True


def test_remove_duplicates_normal():
    assert remove_duplicates("hello") == "helo"


def test_remove_duplicates_all_same():
    assert remove_duplicates("aaaa") == "a"


def test_remove_duplicates_none():
    assert remove_duplicates(None) is None


def test_convert_case_upper():
    assert convert_case("hello", "upper") == "HELLO"


def test_convert_case_lower():
    assert convert_case("HELLO", "lower") == "hello"


def test_convert_case_title():
    assert convert_case("hello world", "title") == "Hello World"


def test_convert_case_none():
    assert convert_case(None, "upper") is None


def test_convert_case_invalid_mode():
    with pytest.raises(ValueError):
        convert_case("hello", "invalid")

项目统计

  • 代码行数:约 150 行(含测试)
  • 函数数量:5 个
  • 测试用例:15+ 个
  • Git 提交:7 次(每个 Task 一次)
  • 开发时间:约 30 分钟
  • Bug 数量:0
  • 测试覆盖率:100%

🎯 Day 2 总结

✅ 完成的目标

  1. ✅ 深入理解 TDD(RED-GREEN-REFACTOR)

    • 看到了每个步骤的细节
    • 理解了为什么要先写测试
    • 体验了 YAGNI 原则
  2. ✅ 理解 Git Worktrees

    • 看到了创建过程
    • 理解了上下文隔离
    • 体验了并行开发
  3. ✅ 完成一个小项目(手动模式)

    • Python 字符串处理工具库
    • 5 个函数,15+ 测试用例
    • 100% 测试覆盖率
  4. ✅ 对比两种模式

    • 自动模式:快速,但看不到细节
    • 手动模式:稍慢,但学习效果好

💡 关键洞察

  1. 测试不是负担,是需求

    • 测试就是需求的精确描述
    • 写测试时更理解功能
    • 测试作为文档使用
  2. Git Worktrees 很强大

    • 上下文隔离,互不干扰
    • 可以并行开发
    • 容易回滚和修改
  3. 手动模式适合学习

    • 每步都清晰可见
    • 完全掌控节奏
    • 学习效果优秀
  4. 根据需求选择模式

    • 大型项目:自动模式
    • 学习理解:手动模式

🚀 准备进入 Day 3

明天学习重点:

  1. 系统化调试(systematic-debugging)

    • 4 阶段根因分析
    • 识别问题 → 定位原因 → 验证假设 → 修复验证
  2. 代码审查流程

    • 请求代码审查(requesting-code-review)
    • 回应审查意见(receiving-code-review)
  3. 完成一个带 Bug 的项目

    • 学习如何系统化调试
    • 练习代码审查

📚 延伸阅读


💬 交流讨论

如果你也在学习 Superpowers,或者有任何疑问,欢迎在评论区交流!

也可以加入 Discord 社区 和其他开发者一起讨论。


📅 系列文章预告

  • Day 1: ✅ Superpowers 入门与第一个项目
  • Day 2: ✅ TDD 深度实践与 Git Worktrees(本文)
  • Day 3: 🐛 系统化调试与代码审查(敬请期待)
  • Day 4: 🚀 自定义技能与高级技巧(敬请期待)

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!

我是竹哥,4 周时间,系统学习 Superpowers,带你从入门到精通。


发布日期:2026-03-22 系列:Superpowers 4 周学习计划 作者:竹哥 项目:Python 字符串处理工具库 技术栈:Python 3.x + pytest