拿到一个新项目后,正式开发前我会做什么?

拿到一个新项目后,正式开发前我会做什么?

以前拿到一个新项目,我很容易直接进入开发:

1
2
3
4
5
6
7
拿到代码
↓
打开 IDE
↓
找一个看起来相关的文件
↓
开始改

后来发现,这样做很容易出现几个问题:

  • 不知道项目到底是怎么运行的
  • 不知道哪些代码是真正的主流程
  • 不知道自己修改的代码会影响什么
  • 不知道原来的功能是否正常
  • 不知道应该怎么验证自己的修改
  • 改到一半才发现自己对原项目的理解是错的

所以我现在更倾向于把:

“拿到项目后做什么”

和:

“开始写代码”

分开。

正式开发前,我需要先把一个陌生项目从:

“我不知道它是什么”

变成:

“我知道它怎么运行、主要流程怎么走、数据怎么流、我要改什么,以及改完以后怎么证明正确。”

所以,我现在会按照下面这条流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
拿到项目
  ↓
认识项目
  ↓
确认怎么运行
  ↓
建立开发环境
  ↓
跑通项目
  ↓
建立原始基线
  ↓
建立项目地图
  ↓
找到入口
  ↓
恢复主流程
  ↓
理解关键数据流
  ↓
确认现有行为
  ↓
定义我要解决的问题
  ↓
确定修改范围
  ↓
定义验收标准
  ↓
开始开发

这套流程不只适用于 Python。

Python、Java、Go、前端、Agent 项目,虽然具体命令不同,但核心思路基本一样。


一、第一步:先搞清楚“这是什么项目”

拿到一个项目后,第一件事不是看代码。

先回答:

1
2
3
4
这个项目解决什么问题?
给谁使用?
主要功能是什么?
最终运行出来是什么?

首先看项目提供的人类可读信息:

1
2
3
4
5
README
项目文档
示例
截图 / Demo
CHANGELOG

例如 GitHub 项目通常先看:

1
sed -n '1,240p' README.md

如果 README 比较长:

1
less README.md

在 less 中常用:

1
2
3
4
5
↑ ↓       上下移动
空格      下一页
b         上一页
/关键字    搜索
q         退出

也可以直接搜索:

1
rg -n "install|usage|quick start|run|test" README.md

不同项目关键词可能不同,不需要机械照抄。

这一阶段只做一件事情:

建立功能层面的认识。

不是研究代码怎么实现。


二、第二步:确认“这个项目怎么运行”

知道项目是干什么的以后,马上确认:

它到底怎么跑起来?

我要找到:

1
2
3
4
5
6
7
8
安装方式
启动命令
依赖
环境变量
配置文件
运行参数
外部服务
数据库

不同技术栈的入口不同。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Python
→ pyproject.toml / requirements.txt

Node
→ package.json

Java
→ pom.xml / build.gradle

Go
→ go.mod

Rust
→ Cargo.toml

这里不要把这些文件理解成“程序本身”。

它们更像是:

告诉我这个项目需要什么环境、怎么安装、怎么启动。

例如 Python 项目可以先看:

1
sed -n '1,240p' pyproject.toml

Node 项目:

1
cat package.json

Java Maven:

1
cat pom.xml

三、第三步:确认当前目录和 Git 状态

真正开始操作前,先确认自己现在在哪里。

1
2
3
4
pwd
ls -la
git status
git branch --show-current

这一步非常简单,但是开发前非常重要。

因为我需要先确认:

1
2
3
我现在是不是在项目根目录?
当前是什么分支?
有没有已有修改?

如果是别人已经做过一部分工作的仓库,尤其要注意:

1
git status

不要把别人已有的修改误认为自己的。


四、第四步:建立独立开发环境

根据项目要求建立环境。

Python 常见:

1
2
python3.11 -m venv .venv
source .venv/bin/activate

Node 可能是:

1
npm install

或者:

1
pnpm install

Go 通常:

1
go mod download

Java 则通常由 Maven / Gradle 管理依赖。

这一阶段的核心目标不是学工具,而是:

让项目在一个可控、可重复的环境里运行。

同时确认:

1
2
3
4
5
版本
依赖
环境变量
配置
外部服务

Python 中可以检查:

1
2
3
python --version
which python
python -m pip --version

Node 可以检查:

1
2
node --version
npm --version

Go:

1
go version

五、第五步:先把原项目跑起来

现在先不要改代码。

先实际运行原项目。

至少完成:

1
2
3
4
启动
主要功能
一个正常场景
一个典型场景

比如:

1
2
3
4
5
6
7
8
CLI
→ 执行一次命令

Web
→ 启动服务并完成一次请求

Agent
→ 完成一次完整任务

如果项目运行失败,也不要马上修改源码。

先判断:

1
2
3
4
5
是环境问题?
是缺少依赖?
是配置问题?
是外部服务没启动?
还是项目本身真的有问题?

可以通过:

1
2
git status
git log -1

确认自己没有修改项目。

这一阶段的目标是:

知道原项目在真实环境中是怎么工作的。

因为:

1
2
3
4
5
文档
→ 作者希望它怎么工作

实际运行
→ 它现在实际上怎么工作

两者可能并不完全一致。


六、第六步:跑测试,建立原始基线

项目能运行以后,再运行现有测试。

常见:

1
pytest

或者:

1
npm test

或者:

1
go test ./...

Java:

1
mvn test

测试结果要记下来。

例如:

1
2
3
4
5
6
原始基线
---------
功能:正常
测试:318 passed
版本:xxxx
环境:xxxx

这叫:

Baseline(基线)

以后:

1
2
3
4
5
6
7
原项目
↓
开始修改
↓
再次测试
↓
对比

如果:

1
318 passed

变成:

1
2
312 passed
6 failed

至少说明:

我的修改影响了原有行为。

注意:

测试通过 ≠ 项目绝对正确。

它只是说明:

在已有测试覆盖到的范围内,没有发现新的失败。

所以基线是开发安全线,不是正确性的终极证明。


七、第七步:建立项目地图

项目已经能运行以后,再开始看代码结构。

先看目录:

1
find . -maxdepth 3 -type d | sort

看源码:

1
find . -type f | sort

Python 可以:

1
find src -type f -name "*.py" | sort

Node 可以重点看:

1
2
3
4
5
src/
app/
pages/
components/
server/

Java 可以看:

1
2
src/main/
src/test/

Go 可以看:

1
2
3
cmd/
internal/
pkg/

这里先不要逐个文件阅读。

只回答:

1
2
3
4
5
6
源码在哪里?
测试在哪里?
配置在哪里?
入口在哪里?
核心业务在哪里?
工具在哪里?

然后给主要目录贴职责标签。

例如:

1
2
3
4
5
6
7
8
9
src/
├── main.py       → 入口
├── agent.py      → 核心逻辑
├── tools/        → 工具
├── config.py     → 配置
└── ui.py         → 用户交互

tests/
└── ...           → 测试

这一阶段的目标是:

建立项目地图,而不是读懂所有代码。


八、第八步:找到程序入口

接下来要回答:

用户做了什么以后,程序从哪里开始执行?

不同项目的入口方式不同。

Python:

1
2
3
4
grep -nA10 "\[project.scripts\]" pyproject.toml
grep -Rni "def main" src
grep -Rni "def cli" src
grep -Rni "__main__" src

Node:

1
cat package.json

重点找:

1
2
3
4
"scripts": {
  "start": "...",
  "dev": "..."
}

Java:

寻找:

1
public static void main(...)

Go:

查看:

1
2
cmd/
main.go

不要只找到文件名就停下来。

最终要回答的是:

1
2
3
4
5
用户操作
↓
入口文件
↓
入口函数

九、第九步:恢复主调用链

找到入口以后,开始追:

入口调用谁?谁又调用谁?

例如:

1
2
3
4
5
6
7
8
9
CLI
 ↓
Application
 ↓
Service
 ↓
Repository
 ↓
Database

或者 Agent:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
用户输入
 ↓
Agent
 ↓
LLM
 ↓
Tool Call
 ↓
Tool
 ↓
Tool Result
 ↓
Agent
 ↓
最终输出

这条链就是:

主执行链。

这里不要马上研究所有分支。

先找到:

1
2
3
4
5
入口
↓
核心
↓
输出

主干恢复以后,再补细节。


十、第十步:同时追踪 Data Flow

只知道:

1
A → B → C

还不够。

还要知道:

1
2
3
A 给 B 什么?
B 改了什么?
B 给 C 什么?

也就是:

数据流。

例如:

1
2
3
4
5
6
7
8
9
用户输入
 ↓
Request
 ↓
Service
 ↓
Database
 ↓
Response

Agent 可能是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
User Message
 ↓
State / Messages
 ↓
Model
 ↓
Tool Call
 ↓
Tool Result
 ↓
State / Messages
 ↓
Model

所以陌生项目至少要建立两个模型:

1
2
3
4
5
Control Flow
→ 谁调用谁?

Data Flow
→ 什么数据在谁之间流动?

只有 Control Flow,容易变成:

我知道程序经过了哪些节点,但不知道每个节点拿到了什么。

只有 Data Flow,也不完整:

我知道数据有什么,但不知道它什么时候流动。


十一、第十一步:确认“哪些代码真的参与当前功能”

项目里有很多代码,但不是所有代码都会参与当前功能。

所以要区分:

1
2
3
4
5
6
7
主流程
辅助模块
兼容代码
未使用代码
测试代码
配置代码
开发工具

不要因为:

“这个类看起来很重要”

就认为它一定重要。

更可靠的问题是:

“它什么时候执行?谁会调用它?”

搜索通常比人工翻文件更快。

例如:

1
rg "ClassName" src tests

查函数:

1
rg "function_name" src tests

查调用:

1
rg "function_name\(" src tests

如果没有 rg,可以用:

1
grep -Rni "function_name" src tests

可以简单记:

1
2
3
4
5
find
→ 找文件

rg / grep
→ 找代码关系

十二、第十二步:看核心测试,确认现有行为

测试不是开发结束后的最后一步。

测试其实也是:

项目作者写出来的行为说明。

例如:

1
2
result = agent.run(...)
assert result == ...

可以反向追:

1
2
3
4
5
6
7
8
9
测试
 ↓
调用哪个函数?
 ↓
输入是什么?
 ↓
预期结果是什么?
 ↓
哪些边界被特别测试?

所以开发前最好看核心测试。

你要逐渐知道:

1
当前系统认为什么叫“正确”?

这是以后修改业务逻辑时非常重要的参照物。


十三、第十三步:把自己的理解写出来,再验证

做到这里,我应该已经有自己的第一版模型。

建议自己先画一张图:

1
2
3
4
5
6
7
8
9
入口
 ↓
模块 A
 ↓
模块 B
 ↓
模块 C
 ↓
输出

旁边再画数据:

1
2
3
4
5
6
7
Input
 ↓
Data A
 ↓
Data B
 ↓
Output

对于自己不能确定的地方,明确标记:

1
2
3
✅ 已确认
❓ 猜测
❌ 不知道

这非常重要。

因为:

“我不知道”和“我猜应该是这样”是两件不同的事情。

然后再通过:

1
2
3
4
5
代码
测试
日志
断点
实际运行

验证。


十四、第十四步:AI 可以参与“项目考古”

这时候 AI 特别有价值。

但建议采用:

1
2
3
我先理解
↓
AI 检查

而不是:

1
2
3
AI 先理解
↓
我直接接受

例如你可以把项目交给 AI:

不修改代码。分析这个项目的主执行链,从用户输入开始,到最终输出结束。给出每个关键节点的文件、函数、输入、输出。无法确认的地方明确标记。

然后把 AI 的结果和自己的流程图对比。

再问:

我认为这里是 A → B → C,你帮我只检查这三个调用关系,并给出代码证据。

这样 AI 更像:

代码考古助手 / 代码审查者。

而不是替你理解整个项目。


十五、第十五步:确认“我要解决的到底是什么问题”

到这里,才开始从:

接手项目

进入:

准备改造项目。

首先明确:

1
2
3
4
5
6
7
8
9
现在是什么?
↓
哪里有问题?
↓
为什么这是问题?
↓
我要改变什么?
↓
哪些行为必须保持不变?

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
现状:
Tool 可以直接执行

问题:
没有统一权限决策点

目标:
所有 ToolCall 执行前经过权限判断

保持不变:
Tool 本身的业务逻辑不改变

这一步非常重要。

因为:

没有问题定义,就没有合理的修改。


十六、第十六步:确定修改范围

现在再开始回答:

1
2
3
4
5
改哪个模块?
新增哪个模块?
删除哪个模块?
哪些文件会受到影响?
哪些模块不应该改?

可以先画:

1
2
3
4
5
原结构
 ↓
我要改变的位置
 ↓
受影响的模块

例如:

1
2
3
Agent
 ↓
Tool

改成:

1
2
3
4
5
6
7
Agent
 ↓
Registry
 ↓
Permission Gate
 ↓
Tool

这里才是正式的:

设计阶段。


十七、第十七步:定义“改完以后怎样才算成功”

不能只说:

“把功能改好。”

必须具体到:

1
2
3
4
5
什么输入
→
应该发生什么
→
应该得到什么结果

例如:

1
2
3
4
5
6
7
8
ALLOW
→ Tool 执行

ASK
→ 暂停等待

DENY
→ Tool 不执行

同时确认:

1
2
3
4
5
6
7
8
原有功能
→ 仍然正常

异常情况
→ 有明确行为

边界情况
→ 有明确行为

这些就是:

验收标准。

最终形成:

1
2
3
4
5
需求
 ↓
实现
 ↓
验证

而不是:

1
2
3
写完代码
↓
感觉应该没问题

十八、AI 可以在设计阶段做什么?

到了这一步,AI 可以大量参与。

例如:

根据当前项目结构,我想解决 X 问题。请提出 3 种架构方案,分别说明优缺点、影响范围、潜在失败模式。不要直接修改代码。

然后我和 AI 讨论:

1
2
3
4
5
6
为什么方案 A?
为什么不是 B?
B 的风险是什么?
如果规模扩大怎么办?
这个模块应该知道什么?
不应该知道什么?

也就是说:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
AI
→ 提案
→ 反例
→ 风险分析
→ 质疑

我
→ 判断
→ 取舍
→ 最终决定

十九、正式开发时,AI 可以高度参与实现

当下面四件事情已经明确:

1
2
3
4
我要解决什么问题
我要改成什么
为什么这样设计
怎样验收

那么具体实现可以大量交给 AI。

包括:

1
2
3
4
5
6
7
8
9
写代码
删模块
新增模块
重构
修改业务逻辑
补测试
修 Bug
处理报错
补文档

这时 AI 主要负责的是:

把已经明确的设计实现出来。

而不是替我决定:

这个项目到底应该是什么。


二十、开发前最后做一次“准入检查”

在真正开始修改之前,我会问自己:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
□ 我知道这个项目做什么
□ 我知道它怎么运行
□ 我已经能正常运行它
□ 我知道原项目基线是什么
□ 我知道程序从哪里进入
□ 我知道主调用链
□ 我知道关键数据怎么流
□ 我知道我要修改哪个位置
□ 我知道为什么要修改
□ 我知道哪些东西不应该被改变
□ 我知道修改后什么算正确
□ 我知道怎么验证

如果前几项没有完成:

继续接手项目。

如果项目已经理解,但问题还没定义:

进入需求 / 设计阶段。

只有这些基本明确:

才开始 Coding。


二十一、把整套流程压缩成一张 SOP

以后拿到任何新项目,我希望自己可以直接照这张走:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
【新项目开发前 SOP】

① 认识项目
□ 项目解决什么问题?
□ 谁使用?
□ 核心功能是什么?

② 确认运行方式
□ 怎么安装?
□ 怎么启动?
□ 需要什么依赖?
□ 需要哪些环境变量?

③ 建立环境
□ 确认版本
□ 建立隔离环境
□ 安装依赖
□ 配置外部服务

④ 跑通原项目
□ 启动
□ 完成基本功能
□ 观察真实行为

⑤ 建立基线
□ 记录版本
□ 记录环境
□ 跑测试
□ 记录测试结果

⑥ 建立项目地图
□ 源码在哪里?
□ 测试在哪里?
□ 配置在哪里?
□ 核心模块在哪里?

⑦ 找入口
□ 用户操作从哪里进入?

⑧ 恢复主流程
□ 谁调用谁?
□ 主流程经过哪些模块?

⑨ 追踪数据
□ 输入是什么?
□ 数据怎么传?
□ State 在哪里变化?

⑩ 确认现有行为
□ 测试怎么写?
□ 边界怎么处理?
□ 哪些代码真正参与运行?

⑪ 定义问题
□ 为什么要改?
□ 当前哪里有问题?

⑫ 定义改造
□ 改什么?
□ 不改什么?
□ 影响哪些模块?

⑬ 定义验收
□ 什么输入?
□ 什么行为?
□ 什么结果?
□ 什么算失败?

⑭ 开始开发
□ 设计确认
□ AI 辅助实现
□ 测试
□ Review
□ 回归验证

二十二、我现在真正想训练的不是“读项目”

以前我会把:

会读项目

理解成:

能看懂别人写的代码。

现在我觉得更准确的定义是:

能够把一个陌生项目,从“我不知道它是什么”,逐渐变成“我知道它怎么运行、核心在哪里、我要解决什么问题,以及怎么验证我的修改”。

所以:

1
读代码

只是其中一环。

完整过程实际上是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
陌生项目
↓
认识
↓
运行
↓
建立基线
↓
建立地图
↓
恢复主流程
↓
理解数据流
↓
确认现有行为
↓
定义问题
↓
定义目标
↓
定义验收
↓
正式开发

这套方法真正有价值的地方在于:

以后换成一个完全陌生的项目,我不需要提前知道它是什么,也不会因为文件很多就无从下手。

我只需要知道:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
先确认什么
→
再运行什么
→
再找什么
→
再验证什么
→
什么时候开始设计
→
什么时候才开始改代码

而不同技术栈的:

1
2
3
4
5
pyproject.toml
package.json
pom.xml
go.mod
Cargo.toml

只是完成“确认这个项目如何被安装、运行和管理”的不同工具。

真正通用的是这套接手思路。

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 Stack 由 Jimmy 设计