如何写好设计文档?
本机暂存
<p>写好一篇设计文档并不是一件容易的事情,本文从 Go 官方 proposal 仓库(<a href="https://github.com/golang/proposal/tree/master/design">golang/proposal/design</a>)中挑选了几个公认优秀、影响深远的设计文档,提炼它们的共性结构与写法,总结成一份可复用的设计文档写作指南,并精炼成一个 <code>to-design</code> skill。</p><span id="more"></span><h2 id="为什么参考-Go-的设计文档"><a href="#为什么参考-Go-的设计文档" class="headerlink" title="为什么参考 Go 的设计文档"></a>为什么参考 Go 的设计文档</h2><p>Go 的设计文档(design proposal)是工程界公认的高质量范本:它们用最朴素的语言讨论最重要的取舍,几乎不堆砌术语,却能让一个陌生读者在十分钟内理解"要解决什么问题、方案长什么样、为什么是这个方案而不是别的"。</p><p>本文参考的 5 个文档:</p><table><thead><tr><th>文档</th><th>主题</th><th>价值</th></tr></thead><tbody><tr><td><a href="https://github.com/golang/proposal/blob/master/design/43651-type-parameters.md">43651-type-parameters</a></td><td>泛型(Go 1.18)</td><td>Go 史上最大的语言变更,复杂特性如何讲清楚的典范</td></tr><tr><td><a href="https://github.com/golang/proposal/blob/master/design/29934-error-values.md">29934-error-values</a></td><td>错误包装 <code>%w</code>/<code>errors.Is</code>/<code>As</code>(Go 1.13)</td><td>标准库 API 设计的范本</td></tr><tr><td><a href="https://github.com/golang/proposal/blob/master/design/60078-loopvar.md">60078-loopvar</a></td><td>循环变量作用域(Go 1.22)</td><td>破坏性变更如何论证兼容性的范本</td></tr><tr><td><a href="https://github.com/golang/proposal/blob/master/design/56345-structured-logging.md">56345-structured-logging</a></td><td>结构化日志 <code>log/slog</code></td><td>新增包的设计与目标论证范本</td></tr><tr><td><a href="https://github.com/golang/proposal/blob/master/design/32437-try-builtin.md">32437-try-builtin</a></td><td><code>try</code> 内建函数(被否决)</td><td>即使被否决,也是讲透取舍的范本</td></tr></tbody></table><hr><h2 id="共性结构:一份设计文档的标准骨架"><a href="#共性结构:一份设计文档的标准骨架" class="headerlink" title="共性结构:一份设计文档的标准骨架"></a>共性结构:一份设计文档的标准骨架</h2><p>把这 5 个文档的章节排在一起,会发现它们高度收敛到同一套骨架。下面是综合提炼出的<strong>通用模板</strong>,每一节都说明它要回答的问题。</p><p><img src="/images/image-20260622225437072.png"></p><h3 id="标题-作者-日期-状态(Title-Author-Date-Status)"><a href="#标题-作者-日期-状态(Title-Author-Date-Status)" class="headerlink" title="标题 / 作者 / 日期 / 状态(Title / Author / Date / Status)"></a>标题 / 作者 / 日期 / 状态(Title / Author / Date / Status)</h3><p>开头永远是元信息。Go 的写法极简:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Title: Proposal: Go 2 Error Inspection</span><br><span class="line">Authors: Jonathan Amsterdam, Russ Cox, Marcel van Lohuizen, Damien Neil</span><br><span class="line">Last updated: January 25, 2019</span><br><span class="line">Discussion at golang.org/issue/29934</span><br></pre></td></tr></table></figure><blockquote><p>关键点:<strong>标题用一句话说清"做什么"</strong>("A built-in Go error check function, <code>try</code>"),并且永远附上讨论入口链接(issue 号),让文档不是孤立的。</p></blockquote><h3 id="摘要-概述(Abstract-Summary)"><a href="#摘要-概述(Abstract-Summary)" class="headerlink" title="摘要 / 概述(Abstract / Summary)"></a>摘要 / 概述(Abstract / Summary)</h3><p>一段话讲完全文。读者读完这一段,应该已经知道你要做什么、大致怎么做。</p><p>泛型文档的摘要开门见山,一句话定义要做的事,紧接着埋下最重要的承诺:</p><blockquote><p>We suggest extending the Go language to add optional type parameters to type and function declarations. ... The design is fully backward compatible with Go 1.</p></blockquote><p>错误处理文档的摘要则点明改动范围和目标:</p><blockquote><p>We propose several additions and changes to the standard library's <code>errors</code> and <code>fmt</code> packages ... codifying error wrapping ...</p></blockquote><blockquote><p>关键点:摘要里就要埋下最重要的承诺(如 "fully backward compatible with Go 1"),它往往是整个设计的隐含约束。</p></blockquote><h3 id="背景-动机(Background-Motivation)"><a href="#背景-动机(Background-Motivation)" class="headerlink" title="背景 / 动机(Background / Motivation)"></a>背景 / 动机(Background / Motivation)</h3><p><strong>用具体的、可感的例子说明"痛在哪"</strong>,而不是抽象地说"现状不好"。这是 Go 文档最值得学的一点。</p><p><img src="/images/image-20260622225601361.png"></p><p>循环变量文档没有讲理论,而是直接甩出一段 bug 代码——往切片里 append 十次 <code>&i</code>,结果十个指针全指向最终值:</p><blockquote><p>Briefly, the problem is that loops like this one don't do what they look like they do:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ids []*<span class="type">int</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> ids = <span class="built_in">append</span>(ids, &i)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>That is, this code has a bug. After this loop executes, <code>ids</code> contains 10 identical pointers, each pointing at the value 10, instead of 10 distinct pointers to 0 through 9.</p></blockquote><p>接着扩展到闭包打印 "3, 3, 3"、表驱动测试 <code>t.Parallel</code> 全部只测了最后一个 case,最后用一句话给这个设计定性:</p><blockquote><p>Loop variables being per-loop instead of per-iteration is the only design decision we know of in Go that makes programs incorrect more often than it makes them correct.</p></blockquote><p>错误处理文档则点明痛点的普遍性——错误包装已经无处不在,普遍到值得收进标准库,同时坦白它的代价:</p><blockquote><p>... one pattern has become so pervasive that we feel it is worth enshrining in the standard library ...</p><p>... indiscriminate wrapping can expose implementation details, introducing undesired coupling between packages ...</p></blockquote><blockquote><p>关键点:<strong>先让读者"疼"起来,方案才有说服力</strong>。用真实代码、真实场景,而不是形容词。</p></blockquote><h3 id="设计-提案(Design-Proposal)"><a href="#设计-提案(Design-Proposal)" class="headerlink" title="设计 / 提案(Design / Proposal)"></a>设计 / 提案(Design / Proposal)</h3><p>文档的主体。共性写法:</p><ul><li><p><strong>从简单到复杂,渐进式教学</strong>。泛型文档从 <code>Print</code> 这种最简单的例子起步,逐步引到泛型图、<code>Map</code>、约束类型推导等复杂场景;它甚至专门提醒读者别把这里的 "generic" 和别的语言混为一谈:</p><blockquote><p>Don't confuse the term generic as used in this design with the same term in other languages like C++, C#, Java, or Rust ...</p></blockquote><p>它把约束定位为"显式定义的结构化约束",而非声明式子类型:</p><blockquote><p>Type constraints are interface types. ... explicitly defined structural constraints.</p></blockquote></li><li><p><strong>每个 API 都配 Go 声明 + 代码示例</strong>。slog 文档对 Logger/Record/Handler 三个核心类型,先给声明再给用法片段(如何用 <code>slog.Group</code> 分组、如何用 <code>LogValuer</code> 脱敏密码)。它对成功的定义也落在"一次安装、处处生效"上:</p><blockquote><p>... the application can create a single handler and install it once for each logging library ... the benefits of a unified backend can be obtained with minimal code churn.</p></blockquote></li><li><p><strong>对照"改造前 vs 改造后"</strong>。<code>try</code> 文档把多行 <code>if err != nil</code> 和一行 <code>f := try(os.Open(filename))</code> 并排展示,收益一目了然。</p></li><li><p><strong>明确边界和约束</strong>。<code>try</code> 文档明确说明:只能在最后返回值为 <code>error</code> 的函数里用、<code>go try(f)</code>/<code>defer try(f)</code> 禁用。划清范围本身就是设计的一部分。</p></li></ul><p><img src="/images/image-20260622225925946.png"></p><blockquote><p>关键点:<strong>声明 + 示例 + 边界</strong>三件套。能用一段可运行代码说明的,绝不用一段文字描述。</p></blockquote><h3 id="理由-取舍(Rationale)"><a href="#理由-取舍(Rationale)" class="headerlink" title="理由 / 取舍(Rationale)"></a>理由 / 取舍(Rationale)</h3><blockquote><p>Rationale 直译是"理由、根据",在设计文档里特指 <strong>"为什么是这个方案,而不是别的"的论证</strong> 。它和"设计"那一节不同:设计讲"方案长什么样",Rationale 讲"为什么这么选"——尤其是对比备选方案、解释取舍。</p></blockquote><p><strong>这是区分"好文档"和"平庸文档"的关键章节</strong>:解释"为什么是这个方案,而不是别的"。</p><p><img src="/images/image-20260622225728666.png"></p><p>好的 Rationale 会主动暴露被放弃的方案及原因。<code>try</code> 文档的 "Design iterations" 一节完整记录了取舍路径——早期版本带一个用户自定义错误处理器,后来被砍掉:</p><blockquote><p>... the context-sensitivity of <code>try</code> was considered fraught ...</p><p>... in the current iteration, rather than introducing a second built-in, we decided to remove the dual semantics of <code>try</code>.</p></blockquote><p>错误处理文档同样解释了演进中改变的选择:为什么用 <code>%w</code> 显式 opt-in 包装,而不是默认包装——因为默认包装会泄露被包装的类型:</p><blockquote><p>... doing so would effectively change the exposed surface of a package by revealing the types of the wrapped errors ...</p><p>... we require that programmers opt in to wrapping by using the new formatting verb <code>%w</code>.</p></blockquote><p>泛型文档甚至专门有 "Discarded ideas"(被丢弃的想法)小节,第一条就是 "What happened to contracts?"——坦白早期草案用过一个叫 contracts 的语言构造,后来换成了接口类型:</p><blockquote><p>An earlier draft design of generics implemented constraints using a new language construct called contracts.</p><p>Type sets appeared only in contracts, rather than in interface types.</p></blockquote><blockquote><p>关键点:<strong>主动写"我们没选什么、为什么没选"</strong>。这比只论证你选的方案更有说服力,也帮后人避免重复讨论。</p></blockquote><h3 id="兼容性(Compatibility)"><a href="#兼容性(Compatibility)" class="headerlink" title="兼容性(Compatibility)"></a>兼容性(Compatibility)</h3><p>凡涉及破坏性变更,必须正面回应兼容性。循环变量文档是这方面的范本——它干脆把兼容性和 rationale 合成一节,开门见山地承认"这是破坏性变更":</p><blockquote><p>In most Go design documents, Rationale and Compatibility are two distinct sections. For this proposal, considerations of compatibility are so fundamental that it makes sense to address them as part of the rationale. To be completely clear: <em>this is a breaking change to Go</em>.</p></blockquote><p>然后用三个机制把破坏性降到最低,并保证老代码原样运行:</p><blockquote><p>... it only applies the new semantics to new programs, so that existing programs are guaranteed to continue to execute exactly as before.</p></blockquote><ul><li>通过 <code>go.mod</code> 里的 <code>go</code> 版本行<strong>按模块 opt-in</strong>。</li><li>用 <code>//go:build</code> 支持<strong>按文件逐步迁移</strong>(gradual code repair)。</li><li>把破例(打破"语言不重定义"的通则)说明为一次性的、深思熟虑的例外,理由是循环变量是 Go 里<strong>唯一</strong>一个"让程序错的次数多于对的次数"的设计。</li></ul><p>错误处理文档则简洁声明:变更不违反 Go 1 兼容性承诺,同时<strong>诚实指出</strong>两处代价:</p><blockquote><p>Gathering frame information may slow down <code>errors.New</code> slightly, but this is unlikely to affect practical programs.</p><p>Errors constructed with <code>errors.New</code> and <code>fmt.Errorf</code> will display differently with <code>%+v</code>.</p></blockquote><blockquote><p>关键点:<strong>诚实</strong>。承认代价(性能、行为变化),给出渐进迁移路径,用先例佐证。</p></blockquote><h3 id="实现-过渡计划(Implementation-Transition)"><a href="#实现-过渡计划(Implementation-Transition)" class="headerlink" title="实现 / 过渡计划(Implementation / Transition)"></a>实现 / 过渡计划(Implementation / Transition)</h3><p>如何落地、分几步、配套什么工具。</p><ul><li><p>错误处理文档:发布 <code>golang.org/x/xerrors</code> 兼容老版本,计划在 Go 1.13 周期初落地。</p></li><li><p>循环变量文档:提供静态编译标志和动态二分工具,并用 Google 内部全量启用的实测数据支撑"风险可控"——失败率约 1/8000,且几乎都是本就有 bug 的测试:</p><blockquote><p>We have used this dynamic tooling in a conversion of Google's internal monorepo to the new loop semantics. The rate of test failure caused by the change was about 1 in 8,000.</p></blockquote><p>它还配了两个工具:静态标志 <code>-d=loopvar=2</code> 报告受影响的循环,动态工具 <code>bisect</code> 用二分法定位出问题的具体那一行。</p></li></ul><blockquote><p>关键点:<strong>用数据和工具支撑"可落地"</strong>。Google 内部实测数据让"风险可控"从口号变成事实。</p></blockquote><h3 id="附录(Appendix):完整-API-示例-FAQ"><a href="#附录(Appendix):完整-API-示例-FAQ" class="headerlink" title="附录(Appendix):完整 API / 示例 / FAQ"></a>附录(Appendix):完整 API / 示例 / FAQ</h3><p>把会打断主线阅读的细节后置:</p><ul><li><p>slog 文档用附录放完整的包文档(含 <code>TextHandler</code>/<code>JSONHandler</code> 输出样例)。它在正文里讨论 Level 取值时,把背后的三条约束讲得很透——这种"看似随意的数字其实每一个都有理由"的写法值得学:</p><blockquote><p>Since Levels are ints, Info is the default value for int, zero.(Info=0,是 int 零值,做默认级别最自然)</p><p>Negating a verbosity converts it into a Level.(用取负兼容 verbosity 风格的日志库)</p><p>Our gap of 4 matches OpenTelemetry's mapping.(级别间隔 4,对齐 OpenTelemetry)</p></blockquote></li><li><p><code>try</code> 文档用 FAQ 回应高频质疑,比如"为什么不用 Rust 的 <code>?</code>"和"<code>try</code> 和异常处理有何不同":</p><blockquote><p>Go has been designed with a strong emphasis on readability ... using <code>?</code> would introduce a new post-fix operator into the language.</p><p><code>try</code> is simply syntactic sugar ... There is also no mechanism to "catch" an error. ... In summary, <code>try</code> is a shortcut for a conditional <code>return</code>.</p></blockquote></li></ul><blockquote><p>关键点:<strong>主线保持流畅,细节进附录/FAQ</strong>。</p></blockquote><hr><h2 id="一页纸模板(可直接复制)"><a href="#一页纸模板(可直接复制)" class="headerlink" title="一页纸模板(可直接复制)"></a>一页纸模板(可直接复制)</h2><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">Title: Proposal: <一句话说清做什么></span><br><span class="line">Author(s): <作者></span><br><span class="line">Last updated: <日期></span><br><span class="line">Discussion at <span class="language-xml"><span class="tag"><<span class="name">issue</span> / <span class="attr">讨论链接</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="section">## Abstract</span></span><br><span class="line"><一段话讲完全文:做什么、大致怎么做、最重要的承诺></span><br><span class="line"></span><br><span class="line"><span class="section">## Background / Motivation</span></span><br><span class="line"><用具体代码/场景说明痛点。让读者先"疼"起来></span><br><span class="line"></span><br><span class="line"><span class="section">## Design / Proposal</span></span><br><span class="line"><从简单到复杂的渐进示例></span><br><span class="line"><每个 API:Go 声明 + 用法片段></span><br><span class="line"><改造前 vs 改造后对照></span><br><span class="line"><明确边界与约束></span><br><span class="line"></span><br><span class="line"><span class="section">## Rationale(理由 / 取舍)</span></span><br><span class="line"><为什么是这个方案></span><br><span class="line"><被放弃的备选方案 + 放弃原因></span><br><span class="line"></span><br><span class="line"><span class="section">## Compatibility</span></span><br><span class="line"><是否破坏性变更,代价是什么,如何渐进迁移></span><br><span class="line"></span><br><span class="line"><span class="section">## Implementation / Transition</span></span><br><span class="line"><落地步骤、配套工具、实测数据></span><br><span class="line"></span><br><span class="line"><span class="section">## Appendix (可选)</span></span><br><span class="line"><完整 API / 端到端示例 / FAQ></span><br></pre></td></tr></table></figure><hr><h2 id="写法上的-7-条核心原则"><a href="#写法上的-7-条核心原则" class="headerlink" title="写法上的 7 条核心原则"></a>写法上的 7 条核心原则</h2><p>把这 5 个文档的"气质"提炼成可执行的原则:</p><ol><li><strong>标题就是结论</strong>。一句话说清做什么,附讨论链接。</li><li><strong>痛点用代码说,不用形容词说</strong>。真实 bug、真实场景最有说服力(见循环变量文档的 <code>&i</code> 例子)。</li><li><strong>渐进式教学</strong>。从最简单的例子起步,复杂概念留到读者有了直觉之后再讲(见泛型文档)。</li><li><strong>声明 + 示例 + 边界</strong>三件套讲 API。能跑的代码胜过一段描述。</li><li><strong>主动暴露被否决的方案</strong>。"我们没选 X,因为 Y" 比单方面论证更可信,也避免后人重复讨论(见 <code>try</code> 的 Design iterations、泛型的 Discarded ideas)。</li><li><strong>诚实面对代价</strong>。性能变慢、输出变化、破坏兼容——都明说,再给迁移路径和先例(见循环变量、错误处理文档)。</li><li><strong>用数据和工具证明可落地</strong>。Google 内部实测的 "1/8000 失败率",比任何"我们认为风险可控"都管用。</li></ol><p><img src="/images/image-20260622230128194.png"></p><hr><h2 id="文本风格:句子、主语与段落怎么写"><a href="#文本风格:句子、主语与段落怎么写" class="headerlink" title="文本风格:句子、主语与段落怎么写"></a>文本风格:句子、主语与段落怎么写</h2><p>结构是骨架,文本风格是肌肉。把这 5 个文档逐句拆开看,会发现它们的"文风"也高度一致——而且每一条都可直接照搬。</p><p><img src="/images/image-20260622230748179.png"></p><h3 id="主语:决策用-We-,行为用-代码本身-,说理用-you"><a href="#主语:决策用-We-,行为用-代码本身-,说理用-you" class="headerlink" title="主语:决策用 "We",行为用"代码本身",说理用 "you""></a>主语:决策用 "We",行为用"代码本身",说理用 "you"</h3><p>Go 文档的人称切换很有讲究:</p><ul><li><p><strong>讲团队的决策和取舍,主语是 "We"</strong>。它把设计说成一群人共同的、可负责的选择,而非客观真理:</p><blockquote><p>We propose several additions and changes ...</p><p>... we decided to remove the dual semantics of <code>try</code>.</p></blockquote></li><li><p><strong>讲代码的行为,主语是代码自己</strong>。让读者把注意力放在程序而非作者身上:</p><blockquote><p>... this code has a bug.</p><p>... loops like this one don't do what they look like they do.</p></blockquote></li><li><p><strong>讲读者会遇到的情况,直接用 "you"</strong>,像面对面解释:</p><blockquote><p>Once you have a test that fails with the new semantics but passes with the old semantics, you run: <code>bisect ...</code></p></blockquote></li></ul><blockquote><p>关键点:<strong>"We" 担责、代码当主角、"you" 拉近距离</strong>。避免"It is suggested that..."这类没有主语、推卸责任的被动腔。</p></blockquote><h3 id="句子:短句下结论,长句讲机制"><a href="#句子:短句下结论,长句讲机制" class="headerlink" title="句子:短句下结论,长句讲机制"></a>句子:短句下结论,长句讲机制</h3><p>Go 文档的节奏感来自<strong>长短句交替</strong>:先用一个极短的句子拍板,再用长句铺开依据。</p><p>循环变量文档最典型——先甩代码,紧接一句话定性,短到不能再短:</p><blockquote><p>That is, this code has a bug.</p></blockquote><p>下结论时句子短、语气硬("<em>this is a breaking change to Go</em>");一旦要解释"为什么不会出问题",就换成信息密集的长句,把条件、机制、后果一次说清:</p><blockquote><p>... it only applies the new semantics to new programs, so that existing programs are guaranteed to continue to execute exactly as before.</p></blockquote><blockquote><p>关键点:<strong>判断用短句,论证用长句</strong>。短句负责让读者记住结论,长句负责让结论站得住。不要通篇都是绕来绕去的长句。</p></blockquote><h3 id="段落:一段一个论点,结论先行"><a href="#段落:一段一个论点,结论先行" class="headerlink" title="段落:一段一个论点,结论先行"></a>段落:一段一个论点,结论先行</h3><p>最值得学的是循环变量文档的 Rationale——它的每个三级小标题本身就是<strong>一个完整的论点句</strong>,段落只是展开论证:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">### A decade of experience shows the cost of the current semantics</span><br><span class="line">### Old code is unaffected, compiling exactly as before</span><br><span class="line">### Changing the semantics is usually a no-op, and when it's not, it fixes buggy code far more often than it breaks correct code</span><br></pre></td></tr></table></figure><p>读者光看标题就能读完整条论证链。每段内部也遵循"结论先行":段首一句给出观点,后面才是例子和数据。</p><blockquote><p>关键点:<strong>一段只讲一件事,观点放段首</strong>。标题能写成一句话的论点,就别写成名词短语(写 "Old code is unaffected" 而不是 "Compatibility")。</p></blockquote><h3 id="语气:克制的诚实,甚至自嘲"><a href="#语气:克制的诚实,甚至自嘲" class="headerlink" title="语气:克制的诚实,甚至自嘲"></a>语气:克制的诚实,甚至自嘲</h3><p>Go 文档不端着。承认代价时直接说 "may slow down <code>errors.New</code> slightly";讲到这个 bug 多普遍时,作者连自己都不放过:</p><blockquote><p>Russ certainly has done it repeatedly over the past decade, despite being the one who argued for the current semantics and then implemented them. (Apologies!)</p></blockquote><p>这种"作者本人也踩过坑"的坦白,比任何"此问题影响重大"的形容词都更有说服力。</p><blockquote><p>关键点:<strong>用事实和自嘲建立可信度,不用形容词堆砌权威感</strong>。强调要克制——<code>_this is a breaking change_</code> 全文就斜体强调这一处,反而格外醒目。</p></blockquote><hr><h2 id="反面提醒:即使被否决,文档也要写好"><a href="#反面提醒:即使被否决,文档也要写好" class="headerlink" title="反面提醒:即使被否决,文档也要写好"></a>反面提醒:即使被否决,文档也要写好</h2><p><code>try</code> 提案最终被社区否决了,但它的设计文档依然是范本——因为它把每一个取舍都讲透了。<strong>文档的价值不取决于提案是否通过,而取决于它是否让讨论变得高质量。</strong> 一份好的设计文档,哪怕方案最后没被采纳,也会成为后人理解"这条路为什么走不通"的权威参考。</p><p><img src="/images/image-20260622230957656.png"></p><blockquote><p>写设计文档的终极目的不是"说服别人同意你",而是"让所有人在同一个事实和取舍基础上做决定"。</p></blockquote><h2 id="培养软实力和编写捷径"><a href="#培养软实力和编写捷径" class="headerlink" title="培养软实力和编写捷径"></a>培养软实力和编写捷径</h2><p>看到这里,你已经了解了写好设计文档的一些思路和方法,你可以审视自己以前写的文档,也可以思考和动手写一篇新的设计文档验证自己的思路。</p><p>AI时代,我们可以让大模型遵循上面的指导原则,帮助我们来写设计文档,可以极大的减少我们的工作量。</p><p>设计文档是给程序员看的,而SPEC是给智能体看的。</p><p>我在<a href="https://goal.rpcx.io/">goal-workflow</a> AI开发流程中增加一个 <code>to-design</code>技能,它可以根据PRD(需求文档, <code>prd</code> 生成)生成设计文档。</p><p>比如昨天我给rpcx增加了一个新特性,使用 <code>gordma</code> 库替换原先的不成熟的<code>rsocket</code>库,以便让rpcx这个框架支持RDMA通讯,生成的设计文档如下:<a href="https://github.com/smallnest/rpcx/blob/master/tasks/design-rdma-conn-transport.md">https://github.com/smallnest/rpcx/blob/master/tasks/design-rdma-conn-transport.md</a> , 生成的这个设计文档颇具Go团队的风格,而且设计思路非常的清晰全面。</p><p><img src="/images/image-20260622231932970.png"></p>
同分类推荐文章
- Designing With Uncertainty: How AI Supercharges Probabilistic Thinking (2026-06-16 23:00:00)
- The Benefits Of Cognitive Inclusion In UX Research (2026-06-10 18:00:00)
- Day for Night (2026-05-29 17:57:14)
建议继续学习
- 哪本书是对程序员最有影响、每个程序员都该阅读的书? (累计阅读 15,096)
- 看源代码那些事 (累计阅读 10,577)
- 最常被程序员们谎称读过的计算机书籍 (累计阅读 9,139)
- 腾讯抄你肿么办 (累计阅读 7,734)
- 低级程序员和高级程序员的区别 (累计阅读 5,786)
- 从代码看不同层次程序员的进化 (累计阅读 5,647)
- 领导如何应对员工离职 (累计阅读 5,471)
- 对程序员职业的一些建议 (累计阅读 5,092)
- 如何设计一个优秀的API (累计阅读 4,854)
- IE的Get请求(URL)的最大长度限制 (累计阅读 4,833)