Skip to content

immars/how-to-write-your-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 

Repository files navigation

How To Write Your Tutorial

此教程的读者

既然你在看这个教程,那么至少你是一个正在写,或者希望写出对读者友好的教程的人。

目前能看到的一个问题是,目前产品/系统的文档主要由开发这个产品/系统的技术人员所产生;而不同技术人员产出的文档所收到的反馈看起来参差不齐。究其原因,最直接的一个问题是文档中教程的部分质量差距比较大。

所以,我进一步假设,作为读者的你,是一个工程师;你正在为你所开发的产品/系统写文档,其中包含了教程的部分。

其实,开源世界里面存在很多优秀的文档和教程的示例,如mxnet/tensorflow等等;如果你认为你可以做到不比他们差,那你可以忽略此教程了。如果你的文档和教程没有收到读者好的反馈,那你可以读一下此教程,希望能对你有所帮助。

为什么要写教程

文档,或者用户手册,可以包含很多内容:架构介绍;功能介绍;API参考手册;常见问题,等等。丰富的文档是用户掌握你想介绍的产品的必要条件。

但是,回想你所学习过的第三方库/框架的过程,第一个打开的,往往是文档中教程的部分:Tutorial, Getting Started, Beginner's Guide, etc. 其原因在于,教程是完整文档的部分选择和精心组织,其核心目的在于:为读者搭建合适的学习曲线。特别是当你做的产品/系统比较复杂的时候,这就显得更重要了。学习曲线,是好的教程和差的教程的主要区别,也是本教程的主要介绍内容。

作为开发人员,写教程对于你自己的一个好处是从另一个视角,整理和理解整个系统。比较大规模的产品往往是很多工程师合作的结果,也许你是项目组中的一员,以前只对自己的那部分内容熟悉;但如果你能写出读者所称赞的教程,就说明你对产品的整体理解没有问题。通常,你会发现写教程的过程也是你对系统加深理解的过程。这件事情前人早有发现,也被称为费曼学习法

更进一步说,培养用户视角有助于设计出更好、更易用的产品。假如无论如何也写不出用户容易理解的手册,如果不是写作技巧的问题,那只能说明产品设计出了问题。无论你的产品是一个程序库,还是一个应用程序,或者是一个工具软件,还是硬件;用户视角都是非常重要的。

让我们开始

接下来的内容,会解释如何写出一个教程。

定义你的读者

这是首先要做的事情。你的读者是什么样的人?他有什么样的知识储备?前面说到,教程的目的是为读者搭建合适的学习曲线。如果你不知道读者是谁,那你连学习曲线的起点都不知道,又谈何合适的学习曲线呢?

读者的定义可以写在教程的开始,如前文所示;也可以不用写。无论写不写,你自己都需要对读者的定义做到心中有数。有时候,你可以在教程中加入知识储备的要求;例如:“需要熟悉python” “了解深度学习的原理” “不要求了解GPU加速原理,但是如果知道的话,会对理解有帮助” “欲练BB,必先AA”,等等。

你是个工程师。你的读者可能也是工程师。但是,工程师和工程师之间的技能水平、背景知识之间的差异是巨大的。请思考一下作为读者的工程师是什么样子。如果可以的话,去见一下真实的客户,会有一定的帮助。

你可以去定义你的读者和你差别很大,也可以跟你技能差不多。但是别忘了最基础的一点:在开始阅读教程之前,你的读者,对于你要介绍的产品的了解为零。这是你的读者和你自己之间最重要的差别。不要假设读者之前通过别的渠道了解过你的产品的任何信息——哪怕真的是这样。如果你要介绍的产品有更广为人知的类似产品,这对教程的写作有帮助,你可以将这一点也加入读者定义里面。

解释为什么

在开始的时候解释清楚“为什么”会很有帮助。为什么你要开发这个产品?它解决什么问题?它之前,这个问题是什么样子,它的出现希望怎么解决这个问题?

例子如前文所述。

再举个例子,Cuda 的官方文档。Introduction 1.1 一节并没有一上来先讲Cuda,而是先把背景介绍清楚:从CPU到GPU的演变,引起了并行编程的需求,从而引出了Cuda。

了解产品出现背后的原因,是了解产品的第一步。这个时候,你的产品对于读者来说是未知的,但是至少你想解决的问题对他来说应该是已知的,否则就不应该是你的读者。这是学习曲线的最低点。

一次引入一个概念

一次引入一个概念,是保持学习曲线平缓的最重要的一点。你的产品里面可能有不少精妙的设计,或者你的产品里面提供了丰富的功能,迫不及待要展示给用户了;但是先等一下!从零开始,从最基础的概念开始,一个一个地解释给用户。

举一个细节的例子。再回到Cuda的官方文档,注意图3图5图6图7

可以看出,上面几幅图,看起来都是在讲Cuda的编程模型和GPU的架构,而且呈现出越来越多的细节。相信实际的完整GPU的架构图远比上面四个更复杂。但是作者先给读者呈现的是图3,这里面没有出现任何GPU和Cuda专属的概念,只是从CPU的概念延伸出了GPU从运行和编程模式上的不同;到了图5,作者希望解释cuda所提出的编程模式的可扩展性,于是在图中引入了Block的概念;再到图6,我们才能看到Block和线程的关系;图7,我们才看到内存访问方面的细节。

同样是架构示意图,可以看到,如非必要,作者不会在读者毫无准备的情况下,抛出新的概念/名词;每次新出现的概念,都是从全局到局部、从概略到细节的顺序下,读者在当时最需要了解到的概念。

请注意这个顺序:从全局到局部、从概略到细节。通常来说,读者了解某个细节的前提条件是先了解较为整体的概念,否则会陷入疑惑之中:这是什么/这和其他东西有什么关系/为什么我需要知道这个?

如果你在介绍一个概念的时候,不得不出现了另一个读者可能不知道的概念,那你需要考虑如何在教程的前面找地方合理地引入这个概念。

手把手的操作指导

对于开发库/工具软件,最好的学习方式就是动手实践;因此,好的教程可以指导读者在实际的操作中,把产品用起来。

这方面的例子非常多:几乎每个质量不错的开源软件,都会在教程里说明如何准备环境,安装/编译,最后跑起来。

对于比较复杂的产品,特别是同时涉及到软件和硬件的产品,需要注意确认:读者作为用户,在阅读教程的时候,实际手里能拿到的硬件是什么样子,有什么周边设备;预装的软件有些什么,等等。从这一点出发,所有用户需要做的操作,都需要出现在操作指引当中。这方面的例子有Arduino,当然,由于有完善的IDE的存在,这个操作指引显得很简单。你最不希望看到的,是读者拿着你的软硬件,看着你的教程,但是就是无法得到你所描述的结果。

这个环节出问题,可能是作为教程的作者,不小心忽略了前面所述的基本原则:在阅读教程之前,你的读者对于你要介绍的产品的了解为零。有些设置、操作或者处理方式,被熟悉产品的你认为是理所应当,所以不经意间在教程中忽略了,但读者事实上是不知道的。

迭代式介绍

这也是工程师读者所最喜欢的内容组织方式:先引入最少的环节跑起来,再一点点加入更多内容。

所以,很多教程前面的部分在引导用户运行Hello World:先把环境打通,让产品能运行看到效果;然后不断修改Hello World这个例子,不断加入新的内容。

一个例子是bazel:先使用最简单的例子stage1把基本的编译过程跑起来,在这个过程中介绍workspace, package等概念;然后通过更复杂的例子,介绍target之间的依赖关系(stage2),以及如何处理多个package(stage3)。

迭代式的组织方式天然也是适合一次引入一个概念的。

推荐的形式

各种软件和服务都可以被用来写文档和教程。由于你是一个工程师,所以推荐的做法是文档和教程和代码工程放在一起:从README.md开始,以markdown格式书写。

和代码放在一起的好处是维护方便:你在wiki中或者在docx中写的文档,可能会随着代码的改变而变得不准确而失效。和代码放在一起就可以和代码的修改一起进行、一起code review,从而保持文档和代码的同步。

另外,可以利用工具(例如doxygen 或者 sphinx ) 将markdown格式的文档和代码中以注释形式存在的API文档一同导出,形成读者更友好的格式。

About

A tutorial about writing a tutorial

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published