以观察、判断和行动三个面板呈现文档变化的验证过程。

把文档变成可以审阅的代码资产

指南即使和应用放在同一仓库,也可能写着一个从未正确运行过的命令。把文档按代码方式管理,提供的是发现和审阅这类错误的条件:可比较的源文件、版本历史、明确的维护者、页面预览,以及随变更一起运行的检查。它不会自动让文字正确。真正有价值的联系,是把文档中的说法和读者实际使用的那个实现对应起来,并留下能够支持该说法的证据。 为读者使用的版本写清契约 说明应当对应具体的软件版本。开发分支上复制出来的命令,可能使用已发布安装包尚未提供的参数。页面需要让读者看见版本选择;可行时,把实现变化与受影响的操作说明放在同一次审阅中。重点检查默认值、权限、输入文件、预期输出以及完成后需要清理的内容。静态页面构建成功,只覆盖其中一部分,并不能证明操作步骤适合当前软件。 对于项目已经存在权威来源的机械事实,应当复用该来源。依据配置结构生成参数表,可以减少手工维护同一事实的次数,但生成器也可能遗漏默认值、错误理解类型,或者列出最终安装包不包含的选项。因此,生成器自身的变化仍需审阅,重要示例也应与实际构建出的程序核对。自动生成的参考表与面向人的解释解决不同问题,任何一方都不能替另一方保证正确。 坏掉的样子 常见假象是把示例另抄一份写进测试,测试继续通过,而页面上显示的命令已经发生漂移。文档格式允许时,应直接提取展示内容执行;或者让页面和验证共同来自同一个经过检查的输入。还要避免教读者使用没有正式说明的缩写参数:解析器今天可能接受某个前缀,明天增加另一个同前缀参数后,原命令就变得不明确。完整选项通常更容易稳定说明。 执行目录也是契约的一部分。作者电脑里已有的文件、缓存的依赖或继承的凭据,都可能让步骤只在作者环境里成功。可复现实验应创建自己的无害输入,并说明工具前提。不能因为代码块在 Markdown 中,就用发布凭据执行任意贡献者提交的 shell 片段;文档检查只应拥有示例实际需要的权限。权限隔离不能替代结果检查,但能让验证边界更明确。 链接、拼写和格式检查都有价值,却不能判断步骤是否解决了读者的问题。命令可能退出成功,结果顺序却不对,或者修改了另一份文件。应当核对有业务意义的输出,保留对使用前提、失败恢复和目标读者起点的人工审阅。否则,一排绿色检查只能证明某些规则满足了,不能证明整篇指南已经成为可信操作手册。 快速验证 将下面代码保存为 docs-code.py,执行 python3 docs-code.py。示例只使用 Python 标准库和临时目录。它先创建键顺序为 z、a 的 JSON 输入,再创建一份简短 Markdown 指南。指南中的 fenced 命令故意使用不存在的 --sort-fields 参数,让错误发生在真正展示给读者的命令上,而不是另写一段与文档无关的失败测试。 from pathlib import Path from tempfile import TemporaryDirectory import difflib import json import re import shlex import subprocess import sys with TemporaryDirectory() as directory: root = Path(directory) (root / 'sample.json').write_text('{"z":2,"a":1}\n') doc = root / 'guide.md' old = '# Format JSON\n\n```sh\npython3 -m json.tool --sort-fields sample.json\n```\n' doc.write_text(old) def execute_document(): command = re.search(r'```sh\n([^\n]+)\n```', doc.read_text())[1] args = shlex.split(command) assert args[:3] == ['python3', '-m', 'json.tool'] return subprocess.run([sys.executable, *args[1:]], cwd=root, capture_output=True, text=True) failed = execute_document() assert failed.returncode == 2 and 'unrecognized arguments' in failed.stderr doc.write_text(old.replace('--sort-fields', '--sort-keys')) passed = execute_document() assert passed.returncode == 0 assert list(json.loads(passed.stdout)) == ['a', 'z'] delta = ''.join(difflib.unified_diff(old.splitlines(True), doc.read_text().splitlines(True), fromfile='before/guide.md', tofile='after/guide.md')) print(json.dumps({'broken_command_exit': failed.returncode, 'corrected_command_exit': passed.returncode, 'parsed_keys': list(json.loads(passed.stdout)), 'doc_diff': delta})) 脚本提取代码块里的单行命令,不经过 shell,而是拆成参数列表,调用当前解释器实际提供的 JSON 工具。第一次运行应以状态码二退出,并报告无法识别的参数。随后把文档修正为 --sort-keys,再次提取并执行。第二次必须成功,解析输出后的键顺序也必须变成 a、z;只看退出码无法证明排序要求已经满足。 ...

三月 29, 2027 · 1 分钟 · XB Field Notes