Overview
What should be recorded is the decision, not just the code. The most valuable documentation records the "why"—the context, constraints, and trade-offs at the time a decision was made. Code shows "what was done"; documentation explains "why it was done this way" and "what other options were considered." This context is indispensable for the people and Agents who subsequently work in this codebase.
When to Use
- Making an important architectural decision
- Choosing among several competing options
- Adding or changing a public API
- Shipping a feature that changes user-visible behavior
- Helping new members (including Agents) get familiar with the project
- Finding yourself repeatedly explaining the same thing
When Not to Use:Do not document self-evident code. Do not write comments that merely restate the code itself. Do not document throwaway prototypes.
Architecture Decision Records (ADRs)
An ADR records the reasoning behind important technical decisions. It is the highest-return type of documentation you can write.
When to Write an ADR
- Selecting a framework, library, or important dependency
- Designing a data model or database schema
- Selecting an authentication strategy
- Deciding on API architecture (REST, GraphQL, tRPC, and the like)
- Choosing among build tools, hosting platforms, or infrastructure
- Any decision that is difficult to reverse once made incorrectly
Where ADRs Are Stored
ADRs are stored under docs/architecture/ , with filenames using ADR-<序号>-<简短标题>.md(for example, ADR-001-use-postgresql.md). The numbers increase consecutively, and theADR- prefix makes it immediately recognizable in docs/architecture/ even when placed alongside other design documents such as module layout and data flow.
By default, a flat layout is sufficient. When the number of ADRs grows and categorization by topic becomes clearer, subdirectories can be created as needed under docs/architecture/ (for example, data/、auth/), gathering ADRs on the same topic into them; the numbers still remain consecutive globally.
Division of labor with
arch-design:arch-designThearch_design.mdis a design document during development; an ADR is a long-term archive used at the end to solidify decisions that are "too expensive to reverse." The responsibilities of the two do not overlap—one serves the current implementation, the other serves subsequent reasoning.
ADR Template
# ADR-001: 主数据库选用 PostgreSQL
## 状态
已接受 | 被 ADR-XXX 取代 | 已废弃
## 日期
2025-01-15
## 背景
任务管理应用需要一个主数据库。关键需求:
- 关系型数据模型(用户、任务、团队之间有关联)
- 任务状态变更需要 ACID 事务
- 任务内容需要支持全文检索
- 需要有托管服务(团队小,运维能力有限)
## 决策
选用 PostgreSQL,搭配 Prisma ORM。
## 考虑过的备选方案
### MongoDB
- 优点:schema 灵活,起步快
- 缺点:我们的数据本质上是关系型的,得手动维护关联关系
- 否决原因:在文档型存储里放关系型数据,要么导致复杂的 join,要么导致数据冗余
### SQLite
- 优点:零配置、内嵌、读取快
- 缺点:并发写入支持有限,生产环境没有托管服务
- 否决原因:不适合生产环境下的多用户 Web 应用
### MySQL
- 优点:成熟,支持广泛
- 缺点:PostgreSQL 的 JSON 支持、全文检索和生态工具更好
- 否决原因:就我们的功能需求而言,PostgreSQL 更合适
## 后果
- Prisma 提供类型安全的数据访问和迁移管理
- 可以直接用 PostgreSQL 的全文检索,不必再引入 Elasticsearch
- 团队需要具备 PostgreSQL 知识(属于通用技能,风险低)
- 托管在托管服务上(Supabase、Neon 或 RDS)
ADR Lifecycle
提议 → 已接受 → (被取代 或 已废弃)
- Do not delete old ADRs. They carry historical context.
- When a decision changes, write a new ADR and reference and declare it as superseding the old one.
Inline Documentation
When to Write Comments
Comments should explain "why," not "what":
// 反例:复述代码本身
// 计数器加 1
counter += 1;
// 正例:解释不显然的意图
// 限流采用滑动窗口——在窗口边界重置计数器,
// 而不是按固定时刻重置,以防止攻击者在窗口交界处突发请求
if (now - windowStart > WINDOW_SIZE_MS) {
counter = 0;
windowStart = now;
}
When Not to Write Comments
// 不要给不言自明的代码写注释
function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// 不要为"现在就该做的事"留 TODO 注释
// TODO: 加错误处理 ← 现在就加
// 不要留被注释掉的代码
// const oldImplementation = () => { ... } ← 直接删掉,git 里有历史
Recording Known Pitfalls
/**
* 重要:这个函数必须在首次渲染之前调用。
* 如果在 hydration 之后才调用,会出现一次无样式内容闪烁,
* 因为服务端渲染期间主题上下文还不可用。
*
* 完整的设计理由见 ADR-003。
*/
export function initializeTheme(theme: Theme): void {
// ...
}
API Documentation
For public APIs (REST, GraphQL, a library's external interfaces):
Write alongside the types (preferred in TypeScript)
/**
* 创建一个新任务。
*
* @param input - 任务创建数据(title 必填,description 可选)
* @returns 创建出的任务,带服务端生成的 ID 和时间戳
* @throws {ValidationError} 当 title 为空或超过 200 字符
* @throws {AuthenticationError} 当用户未认证
*
* @example
* const task = await createTask({ title: '买菜' });
* console.log(task.id); // "task_abc123"
*/
export async function createTask(input: CreateTaskInput): Promise<Task> {
// ...
}
Use OpenAPI / Swagger for REST APIs
paths:
/api/tasks:
post:
summary: 创建一个任务
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTaskInput'
responses:
'201':
description: 任务已创建
content:
application/json:
schema:
$ref: '#/components/schemas/Task'
'422':
description: 校验错误
README Structure
Every project should have a README covering the following:
# 项目名称
一段话说明这个项目是做什么的。
## 快速开始
1. 克隆仓库
2. 安装依赖:`npm install`
3. 配置环境:`cp .env.example .env`
4. 启动开发服务器:`npm run dev`
## 命令
| 命令 | 说明 |
|---|---|
| `npm run dev` | 启动开发服务器 |
| `npm test` | 运行测试 |
| `npm run build` | 生产构建 |
| `npm run lint` | 运行 linter |
## 架构
简要说明项目结构和关键设计决策。详情链接到对应 ADR。
## 贡献指南
如何贡献、编码规范、PR 流程。
Changelog maintenance
For released features:
# Changelog
## [1.2.0] - 2025-01-20
### 新增
- 任务分享:用户可以把任务分享给团队成员 (#123)
- 任务指派的邮件通知 (#124)
### 修复
- 快速连点创建按钮时出现重复任务 (#125)
### 变更
- 任务列表每页加载量从 20 改为 50,体验更好 (#126)
Documentation for Agents
Special considerations for AI Agent context:
- Instruction files (AGENTS.md / CLAUDE.md, etc.)— record project conventions so Agents follow them
- spec files— keep the spec up to date so Agents build the right things
- ADR— help Agents understand why past decisions were made the way they were (avoid re-litigating settled matters)
- Inline pitfalls— keep Agents from falling into known traps
common self-justifications
| Self-justification | Reality |
|---|---|
| "The code is self-explanatory" | The code shows "what was done." It doesn't show "why," which alternatives were rejected, or what constraints exist. |
| "Wait until the API stabilizes before writing docs" | An API stabilizes faster once it's written down. Writing docs is the first test of the design. |
| "Nobody reads the docs" | Agents will read them. Later engineers will read them. You yourself will read them three months from now. |
| "ADRs are extra overhead" | A 10-minute ADR can save a 2-hour argument over the same decision six months later. |
| "Comments go stale" | Comments explaining "why" are stable. Comments explaining "what was done" are the ones that go stale—so only write the former. |
Red flags
- Architecture decisions have no written rationale
- Public APIs have no documentation and no types
- The README doesn't clearly explain how to get the project running
- Commented-out code is used instead of deletion
- TODO comments have been sitting for weeks
- The project has important architectural choices but no ADRs
- The docs restate the code instead of explaining intent
Verification
After writing docs:
- All important architectural decisions have corresponding ADRs
- The README covers quick start, commands, and architecture overview
- API functions have documentation for parameters and return types
- Known pitfalls are recorded inline where they actually matter
- No leftover commented-out code
- Instruction files (AGENTS.md / CLAUDE.md, etc.) are up to date and accurate