如何给教程配一个可复现的示例仓库结构
说明一套适合教程配套的目录、锁定依赖、数据获取脚本和 README 写法,让读者按同样的步骤在自己的电脑上跑通示例,且不会把密钥或大模型文件提交进仓库。
一篇教程写得再清楚,如果配套代码缺少版本锁定、目录说明或数据来源,读者仍可能在另一步上卡住。可复现不是把整台电脑打包分发,而是把「环境、步骤、输入、预期输出」写到别人可以执行的程度。本文给出一套适合教程示例的仓库结构,以及不该提交进 Git 的内容。
先确定仓库要承诺什么
在建目录之前,先用四句话写清这个示例的边界:
- 它演示哪一个具体任务,而不是「某个领域的全部功能」。
- 它支持哪些操作系统和语言版本。
- 读者需要预先准备什么,例如磁盘空间或手动从官方渠道下载的模型。
- 跑通之后,应该看到什么输出。
这四句话稍后会几乎原样进入 README。写不清楚的部分,往往也是你还没有真正复现过的部分。
一套够用的目录
下面这个结构适合大多数命令行或脚本类教程。名字可以改,职责不要混:
example-project/
README.md
LICENSE
CHANGELOG.md
pyproject.toml
uv.lock
.gitignore
.python-version
src/
example_project/
__init__.py
__main__.py
scripts/
download_sample.py
check_environment.py
docs/
walkthrough.md
tests/
test_smoke.py
samples/
README.md
含义很直接:
src/放被演示的代码,避免脚本和可安装包混在根目录。scripts/放「准备环境或下载公开样例」的一次性命令,不把密钥写进去。docs/walkthrough.md用分步说明呼应教程正文,可写截图中的命令。tests/至少放一个冒烟测试,确认默认路径能跑通。samples/只放体积很小、许可允许分发的示例输入。大文件不要进仓库。
若示例不是 Python,把 pyproject.toml 和锁定文件换成该语言官方推荐的清单,例如 Node 的锁文件、Rust 的 Cargo.lock。原则相同:声明依赖,并锁定版本。
必须写进 README 的几节
README 不是项目简介海报,而是一份操作说明。建议按这个顺序写:
- 这一示例做什么,不做什么。
- 许可:仓库代码用哪种许可;第三方模型或数据需要读者自行按原许可获取。
- 环境前提:操作系统、语言版本、可选硬件。
- 安装步骤:从克隆仓库到创建环境,命令完整可复制。
- 如何运行默认示例,以及预期输出长什么样。
- 如何运行测试。
- 常见失败:找不到命令、依赖未锁定、样例尚未下载。
- 如何卸载或清理生成的文件。
安装步骤应写成实际命令。以 Python 和 uv 为例(请以你锁定的真实版本为准):
git clone https://github.com/example/example-project.git
cd example-project
uv sync
uv run python -m example_project
把 example 换成你的真实仓库地址。不要只写「按常规方式安装依赖」这种无法执行的句子。
不要提交进 Git 的东西
在 .gitignore 中至少排除:
- 虚拟环境和缓存目录。
- 密钥、令牌、
.env文件。需要时提供.env.example,只放变量名。 - 体积很大的模型权重、数据集和渲染结果。改为在
scripts/download_sample.py里从官方来源下载,并核对校验和。 - 编辑器或操作系统的临时文件。
scripts/download_sample.py 应当:只访问你在 README 中写明的官方地址,下载后计算哈希并与文档中的预期值比较,失败就停止。不要让脚本去爬取许可不明的镜像站。
用一次「他人视角」验收
在仓库发布前,用下面的清单扮演第一次看到它的读者:
- 在一台没有该项目依赖的目录里,只靠 README 从克隆做到运行。
- 关掉你平时导出的环境变量,确认脚本不会依赖机器上的隐式配置。
- 故意不下载大文件,确认程序能给出可读的错误,而不是堆栈的最后一行才提到文件缺失。
- 核对 LICENSE 与第三方声明是否完整。
若你自己都无法在干净的环境里走通,读者更无法做到。把卡住的那一步补回文档,比增加新功能更优先。
示例仓库的目标不是漂亮,而是诚实:它写清边界,锁住版本,把大文件和密钥留在仓库之外,并给读者一条能走完的路。做到这些,教程正文才能真正被「复现」。