拿到一个新项目后,正式开发前我会做什么?
以前拿到一个新项目,我很容易直接进入开发:
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 比较长:
在 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 项目:
Java Maven:
三、第三步:确认当前目录和 Git 状态
真正开始操作前,先确认自己现在在哪里。
1
2
3
4
| pwd
ls -la
git status
git branch --show-current
|
这一步非常简单,但是开发前非常重要。
因为我需要先确认:
1
2
3
| 我现在是不是在项目根目录?
当前是什么分支?
有没有已有修改?
|
如果是别人已经做过一部分工作的仓库,尤其要注意:
不要把别人已有的修改误认为自己的。
四、第四步:建立独立开发环境
根据项目要求建立环境。
Python 常见:
1
2
| python3.11 -m venv .venv
source .venv/bin/activate
|
Node 可能是:
或者:
Go 通常:
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
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
| 文档
→ 作者希望它怎么工作
实际运行
→ 它现在实际上怎么工作
|
两者可能并不完全一致。
六、第六步:跑测试,建立原始基线
项目能运行以后,再运行现有测试。
常见:
或者:
或者:
Java:
测试结果要记下来。
例如:
1
2
3
4
5
6
| 原始基线
---------
功能:正常
测试:318 passed
版本:xxxx
环境:xxxx
|
这叫:
Baseline(基线)
以后:
1
2
3
4
5
6
7
| 原项目
↓
开始修改
↓
再次测试
↓
对比
|
如果:
变成:
至少说明:
我的修改影响了原有行为。
注意:
测试通过 ≠ 项目绝对正确。
它只是说明:
在已有测试覆盖到的范围内,没有发现新的失败。
所以基线是开发安全线,不是正确性的终极证明。
七、第七步:建立项目地图
项目已经能运行以后,再开始看代码结构。
先看目录:
1
| find . -maxdepth 3 -type d | sort
|
看源码:
Python 可以:
1
| find src -type f -name "*.py" | sort
|
Node 可以重点看:
1
2
3
4
5
| src/
app/
pages/
components/
server/
|
Java 可以看:
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
2
3
4
| "scripts": {
"start": "...",
"dev": "..."
}
|
Java:
寻找:
1
| public static void 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
↓
最终输出
|
这条链就是:
主执行链。
这里不要马上研究所有分支。
先找到:
主干恢复以后,再补细节。
十、第十步:同时追踪 Data Flow
只知道:
还不够。
还要知道:
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
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
4
5
| 代码
测试
日志
断点
实际运行
|
验证。
十四、第十四步:AI 可以参与“项目考古”
这时候 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
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
| 原有功能
→ 仍然正常
异常情况
→ 有明确行为
边界情况
→ 有明确行为
|
这些就是:
验收标准。
最终形成:
而不是:
十八、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
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
|
只是完成“确认这个项目如何被安装、运行和管理”的不同工具。
真正通用的是这套接手思路。