为什么技术写作很难
技术写作的难点不在于 Markdown 语法,而在于如何组织信息,让读者能快速找到所需内容并理解。技术文档有一个独特的挑战:你对产品太熟悉,往往无法站在陌生人的角度思考"读者需要什么"。
知识诅咒(Curse of Knowledge)
一旦你掌握了某些知识,就很难想象不知道这些知识是什么感觉。写文档时,作者往往省略了对他们来说"显而易见"的步骤,但读者却卡在这里。解决方法:找实际的陌生人测试你的文档。
好文档的标准
一个完全陌生的程序员,看了你的 README,能在 10 分钟内让项目运行起来;能在 5 分钟内判断这个项目是否适合他的需求;在遇到问题时知道去哪里找帮助。
优秀 README 的结构与原则
README 是项目的"门面",是绝大多数人对你的项目的第一印象。一个好的 README 需要在 30 秒内回答三个问题:这是什么?解决什么问题?我如何使用它?
完整的 README 模板
# 项目名称 > 一句话精准描述:这个项目是什么,解决什么问题,面向谁。 > 例:"轻量级 Go HTTP 框架,专为微服务设计,零依赖,比 Gin 快 3x。" [](LICENSE) [](go.mod) [](actions) [](codecov) ## ✨ 功能特性 - **核心特性 1**:一句话说明价值 - **核心特性 2**:一句话说明价值 - **核心特性 3**:一句话说明价值 ## 🚀 快速开始 ### 安装 ```bash go get github.com/yourname/yourpackage ``` ### 最简示例(Hello World) ```go package main import "github.com/yourname/yourpackage" func main() { r := yourpackage.New() r.GET("/", func(c *yourpackage.Context) { c.JSON(200, map[string]string{"message": "Hello, World!"}) }) r.Run(":8080") } ``` 运行:`go run main.go`,访问 `http://localhost:8080`。 ## 📖 文档 - [完整文档](https://docs.example.com) - [API 参考](https://pkg.go.dev/github.com/yourname/yourpackage) - [示例代码](./examples/) - [迁移指南](./MIGRATION.md) ## 🆚 与同类项目对比 | 特性 | 本项目 | Gin | Echo | | ------------ | :-----: | :----: | :----: | | 零依赖 | ✅ | ❌ | ❌ | | 内存占用 | 8MB | 12MB | 10MB | | 每秒请求数 | 120k | 78k | 95k | | 学习曲线 | 低 | 低 | 中 | ## 🤝 贡献 欢迎 PR 和 Issue!请先阅读 [贡献指南](CONTRIBUTING.md)。 <details> <summary>本地开发环境设置</summary> ```bash git clone https://github.com/yourname/yourpackage cd yourpackage go test ./... # 运行测试 go run ./cmd/main.go # 运行示例 ``` </details> ## 许可证 MIT License — 详见 [LICENSE](LICENSE) 文件。
README 的关键原则
应该包含的内容
- 一句话描述(精准,不废话)
- 快速开始(可运行的示例)
- 安装方法(多种包管理器)
- 核心使用示例
- 文档/API 参考链接
- 贡献指南
- 许可证
应该避免的内容
- 冗长的项目历史背景
- 过度的自我宣传
- 所有功能的完整列表(放 docs 里)
- 过时的截图(随版本更新困难)
- 不会有人阅读的长段介绍
API 文档的标准结构
API 文档(函数、类、HTTP 端点)应该有一致的结构,让读者能快速找到他们需要的信息。
HTTP API 端点文档模板
## `POST /api/users` — 创建用户 创建一个新的用户账户。需要管理员权限。 ### 请求头 | 字段 | 类型 | 必填 | 说明 | | --------------- | -------- | :--: | ------------------------------- | | `Authorization` | `string` | ✅ | Bearer 格式的 JWT Token | | `Content-Type` | `string` | ✅ | 必须为 `application/json` | ### 请求体 ```json { "email": "user@example.com", // 用户邮箱(唯一) "name": "张三", // 显示名称(2-50 字符) "role": "editor", // 可选:admin | editor | viewer(默认 viewer) "send_welcome_email": true // 可选:是否发送欢迎邮件(默认 false) } ``` ### 响应 **成功(201 Created):** ```json { "id": "usr_12345abc", "email": "user@example.com", "name": "张三", "role": "editor", "created_at": "2026-03-26T08:00:00Z" } ``` **错误码:** | 状态码 | 错误码 | 说明 | | ------ | ------------------- | ---------------------------- | | `400` | `invalid_email` | 邮箱格式无效 | | `409` | `email_exists` | 该邮箱已注册 | | `401` | `unauthorized` | 未提供或 Token 无效 | | `403` | `forbidden` | 当前用户无管理员权限 | ### 示例 ```bash curl -X POST https://api.example.com/api/users \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email":"new@example.com","name":"李四","role":"editor"}' ``` ```javascript // JavaScript SDK const user = await client.users.create({ email: "new@example.com", name: "李四", role: "editor" }); ```
函数/方法文档模板
### `getUserById(id, options?)` 根据用户 ID 查询用户信息。优先读取缓存,缓存未命中时查询数据库。 #### 参数 | 参数 | 类型 | 必填 | 说明 | | ------------------ | -------- | :--: | ------------------------------------------- | | `id` | `string` | ✅ | 用户唯一标识符(格式:`usr_` 前缀) | | `options.timeout` | `number` | ❌ | 超时毫秒数(默认 5000) | | `options.useCache` | `boolean`| ❌ | 是否使用缓存(默认 true) | #### 返回值 返回 `Promise<User>`: ```typescript interface User { id: string; // 用户 ID email: string; // 邮箱地址 name: string; // 显示名称 role: UserRole; // 用户角色 createdAt: Date; // 创建时间 } ``` #### 异常 - `UserNotFoundError`:用户 ID 不存在时抛出 - `TimeoutError`:超过 timeout 时间后抛出 - `DatabaseError`:数据库连接失败时抛出 #### 示例 ```typescript // 基本用法 const user = await getUserById("usr_12345"); console.log(user.name); // "张三" // 设置超时 try { const user = await getUserById("usr_12345", { timeout: 2000 }); } catch (e) { if (e instanceof UserNotFoundError) { console.log("用户不存在"); } } // 跳过缓存(强制数据库查询) const freshUser = await getUserById("usr_12345", { useCache: false }); ```
中文技术写作规范
中文技术文档有一套约定俗成的规范,遵循这些规范能显著提升文档的专业感和可读性。
中英文混排规范
# ✅ 正确:中英文之间加空格 使用 JavaScript 开发前端界面。 Python 3.10 以上版本需要安装 numpy 1.24+。 这是一个用 React 和 TypeScript 构建的项目。 # ❌ 错误:中英文紧贴 使用JavaScript开发前端界面。 Python3.10以上版本需要安装numpy1.24+。
INFO("盘古之白"原则)这个规范称为"盘古之白"(Pangu Spacing),得名于为汉字和拉丁字母之间保留空隙的中文排版规范。可以用
pangu.js 或其他语言实现自动添加空格,也有 VS Code 插件在输入时自动添加。
标点符号规范
| 场景 | ✅ 正确 | ❌ 错误 |
|---|---|---|
| 中文句末 | 使用全角句号。 | 使用半角句号. |
| 代码和命令 | 运行 `npm install`,完成后... | 运行`npm install`,完成后... |
| 括号 | (中文括号)用于中文语境 | (中文语境用英文括号) |
| 引号 | "中文引号" 用于中文 | "英文引号" 用于中文 |
| 专有名词 | macOS、iOS、GitHub、JavaScript | MacOS、IOS、Github、Javascript |
| 中文数字 | 共 10 个步骤 | 共10个步骤(中文旁边加空格) |
技术术语的一致性
# 在文档开头或首次出现时明确术语 本文档中,**构建(Build)**指将源代码编译为可执行文件的过程; **部署(Deploy)**指将可执行文件发布到服务器的过程。 # 之后保持一致,不要混用同义词 # ❌ 错误:前面说"构建",后面突然变成"编译" # ✅ 正确:整篇文章统一使用"构建"
段落和句子长度
- 一个段落只讲一件事,聚焦一个主题
- 技术文档每段不超过 5-6 行
- 一个句子表达一个完整意思,不要写很长的从句堆叠
- 复杂的操作流程用有序列表,而不是"首先……然后……最后……"的长句
文档的四种类型(Divio 文档系统)
Divio 公司提出了一个著名的文档分类框架,将技术文档分为四类,每类有不同的目标和写法:
教程(Tutorial)
面向初学者,通过完成一个真实项目来学习。目标:让读者成功运行起来并产生成就感。关键原则:每一步都有明确的结果,读者不需要理解原理就能跟着做。例:Django 官方教程"创建你的第一个投票应用"。
操作指南(How-to Guide)
面向有基础的用户,解决特定任务。目标:让用户完成一个具体目标。关键原则:假设读者已有基础知识,直接给出步骤。例:"如何在 Django 中实现自定义认证"。
参考文档(Reference)
完整的技术规格说明(API 参考、配置项说明)。目标:提供准确完整的信息,供查阅使用。关键原则:完整、精确、结构一致,通常不需要叙述性文字。
解释/概念(Explanation)
深入的概念讲解,帮助读者理解"为什么"。目标:建立理解,而不是完成任务。例:"Django 的 ORM 如何工作"、"JWT vs Session 认证的权衡"。
TIP大多数技术文档的问题在于混淆了这四类文档。教程中加入了太多概念解释(本应是 Explanation),API 参考中写了操作步骤(本应是 How-to)。清晰区分这四类,分别写对应的文档,读者体验会大幅提升。
版本化文档策略
在文档中标注版本信息
# 在 README 顶部标注适用版本 > **版本说明**:本文档适用于 v2.x。 > 如果你使用 v1.x,请查阅 [v1.x 文档](./docs/v1/)。 # 在特定章节标注版本 > **从 v2.3 起可用。** 此功能在早期版本不存在。 # 用 GFM 告警块标注废弃内容 > [!WARNING] > **已废弃(v2.0 移除)**:`oldMethod()` 在 v2.0 中已移除, > 请使用 [`newMethod()`](#new-method) 替代。
CHANGELOG 的标准格式
使用 Keep a Changelog 规范和 语义化版本(Semver):
# 更新日志 所有重要的更改都会记录在此文件中。 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/), 本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 ## [未发布] ### 新增 - 用户个人资料页面支持上传头像 ## [2.1.0] - 2026-03-26 ### 新增 - 支持 OAuth2 第三方登录(GitHub、Google) - 新增批量导入用户 API ### 变更 - 提升密码加密强度(bcrypt 从 10 轮升级到 12 轮) ### 修复 - 修复大文件上传时的内存泄漏问题(#234) - 修复 Safari 下表单提交失败的 bug(#241) ### 废弃 - `User.getProfile()` 已废弃,将在 3.0 中移除, 请使用 `User.profile` 属性替代 ## [2.0.0] - 2026-01-15 ### 破坏性变更(Breaking Changes) - `createUser(name, email)` 签名变更为 `createUser(options: CreateUserOptions)` - 移除对 Node.js 16 的支持,最低版本要求 Node.js 18 [未发布]: https://github.com/user/repo/compare/v2.1.0...HEAD [2.1.0]: https://github.com/user/repo/compare/v2.0.0...v2.1.0 [2.0.0]: https://github.com/user/repo/compare/v1.5.0...v2.0.0
文档健康维护
文档不是一次性工作,需要持续维护。未更新的文档比没有文档更糟糕,因为它会误导读者。
文档腐烂(Documentation Rot)的预防
文档代码化(Docs as Code)
将文档存储在代码仓库中(与代码并排),每次代码变更时更新对应文档,作为 PR 的一部分进行 review。GitHub 会在 PR 中显示 Markdown 文件的变更 diff,和代码 diff 一样清晰。
代码示例的自动化测试
文档中的代码示例应该是可以运行的真实代码,而不是伪代码。最佳实践是将示例代码提取到可执行文件中(如 examples/ 目录),在 CI 中运行这些示例,确保示例随 API 变化一起更新。
定期链接检查
外部链接会随时间失效。使用
markdown-link-check 或 GitHub Actions 定期检查所有链接,修复死链。建议在 CI 中设置每周或每月的定时检查。文档质量检查清单
## 新功能文档 PR 检查清单 - [ ] README 已更新(如果有影响 README 的变化) - [ ] CHANGELOG 已更新 - [ ] API 文档已更新(新增/修改/删除的 API) - [ ] 代码示例可以正常运行 - [ ] 代码示例的注释是最新的 - [ ] 废弃的功能已添加废弃警告 - [ ] 如有破坏性变更,已添加迁移指南 - [ ] 文档中的链接都有效 - [ ] 中英文混排规范符合要求
写给技术写作初学者的建议
从小处做起
- 先写好 README,这是最高 ROI 的文档工作
- 遇到不清楚的地方就补充文档,不要等"以后有时间再写"
- 在 Issue 和 PR 中写清楚问题和解决方案,这些也是文档
参考优秀文档
多读好的技术文档,学习他们如何组织信息:
- Stripe API 文档(业界公认的最佳 API 文档)
- Vue.js 官方文档(中文友好,结构清晰)
- React 官方新文档(互动式学习,概念解释深入)
- Rust 官方书籍《The Rust Programming Language》(错误处理等章节极好)
- GitHub 各大开源项目的 README(如 tailwindcss、prisma)
写作即思考
写文档不只是"把已知的东西记录下来",写作过程本身会帮助你发现设计中的问题:如果你发现某个功能很难解释清楚,往往意味着这个功能的设计不够好,或者 API 不够直观。
写作建议开始写一篇新文档之前,先问自己:这篇文档的读者是谁?他们的知识背景是什么?他们来这里是为了解决什么问题?有了清晰的目标读者,才能写出真正有用的文档。
课程总结
恭喜完成 Markdown 完全指南!你现在系统掌握了:
语法基础(第1-8章)
- 标题、段落、文本格式
- 列表(无序/有序/任务)
- 链接、图片、徽章
- 代码块与语法高亮
- 引用块、分隔线、HTML
- 表格(GFM 扩展)
- Mermaid 图表(6种类型)
- LaTeX 数学公式
工程实践(第9-10章)
- VS Code 配置与插件
- Typora / Obsidian 使用
- Pandoc 格式转换
- MkDocs / Docusaurus 文档站
- markdownlint / Prettier 质量工具
- README 结构模板
- API 文档规范
- 中文写作规范
- CHANGELOG 最佳实践
下一步学了这么多,最重要的是实践。现在就去打开你的一个项目,把 README 改写一遍——按照本章的模板,加上清晰的描述、可运行的示例、正确的徽章。你会发现,用好 Markdown 真的能让你的项目更受欢迎。