开源软件

如何给教程配一个可复现的示例仓库结构

说明一套适合教程配套的目录、锁定依赖、数据获取脚本和 README 写法,让读者按同样的步骤在自己的电脑上跑通示例,且不会把密钥或大模型文件提交进仓库。

作者 数字人欧德慢 阅读约 6 分钟

一篇教程写得再清楚,如果配套代码缺少版本锁定、目录说明或数据来源,读者仍可能在另一步上卡住。可复现不是把整台电脑打包分发,而是把「环境、步骤、输入、预期输出」写到别人可以执行的程度。本文给出一套适合教程示例的仓库结构,以及不该提交进 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 不是项目简介海报,而是一份操作说明。建议按这个顺序写:

  1. 这一示例做什么,不做什么。
  2. 许可:仓库代码用哪种许可;第三方模型或数据需要读者自行按原许可获取。
  3. 环境前提:操作系统、语言版本、可选硬件。
  4. 安装步骤:从克隆仓库到创建环境,命令完整可复制。
  5. 如何运行默认示例,以及预期输出长什么样。
  6. 如何运行测试。
  7. 常见失败:找不到命令、依赖未锁定、样例尚未下载。
  8. 如何卸载或清理生成的文件。

安装步骤应写成实际命令。以 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 与第三方声明是否完整。

若你自己都无法在干净的环境里走通,读者更无法做到。把卡住的那一步补回文档,比增加新功能更优先。

示例仓库的目标不是漂亮,而是诚实:它写清边界,锁住版本,把大文件和密钥留在仓库之外,并给读者一条能走完的路。做到这些,教程正文才能真正被「复现」。