Skip to content

库作者创建信息丰富文档的最佳实践

你为库提供的文档至关重要。 它能决定用户是否会研究你的库,是否会在他们的项目中采用它,以及在遇到困难时是否会坚持下去。 如今的开发者在语言、库、framework 和平台之间有前所未有的选择。 因此,吸引和告知用户至关重要;否则他们可能会寻求其他选项。

在你的库的早期版本中,用户的反馈会很少。 幸运的是,创建和完善文档可以作为一种反馈循环,大大提高你项目的质量。 因此,创建文档绝不应被视为负担,也不应在创建库时被推到优先级列表的末尾。

有效的文档不仅能告知用户,还能推动你的库的开发和完善。 以下是文档指导开发过程的几个关键方式:

  • 你应该能够用几段话解释你的库是做什么的,谁将受益于使用它,以及它相对于替代方法有哪些优势。如果你做不到这一点,请重新考虑你项目的范围和目标。
  • 你应该能够创建一个“入门指南”,让潜在用户尽快上手并运行。尽快的定义将取决于问题领域,但你可以与其他平台上的类似库进行比较。该指南应将用户引入一个反馈循环,该循环应持续变得更简单、更快,同时始终产生可靠的结果。创建此指南将帮助你识别可能阻碍用户进度的复杂性突然增加(cliff edges)。
  • 文档化一个函数(function)的行为会迫使你考虑所有边缘情况,例如有效的输入区间、可能抛出的异常以及工作量增加时性能如何降级。这通常可以改进函数(function)签名和底层实现。
  • 如果初始化你的库所需的代码总是超过完成一项任务所需的代码,请重新思考你的配置选项。
  • 如果你无法创建使用标准选项执行基本任务的清晰示例,请考虑优化你的 API 以适应日常使用。
  • 如果你无法在不使用真实数据源和在线服务的情况下演示如何测试你的库,请考虑为访问网络(以及通常意义上的外部世界)的组件提供测试替身(test doubles)。

你越早为你的库提供文档,它就能越早被实际用户测试。 这些测试的反馈随后可用于改进设计。

提供全面的文档

你的库必须提供足够的文档,以便用户以最小的努力采用它。 此文档应包括:

  • 入门指南
  • API 的深入描述
  • 常见用例的较长示例(也称为“recipes”)
  • 指向博客、文章、网络研讨会和会议演讲等资源的链接

入门指南应涵盖你的库如何与受支持的构建系统集成。 它应包含对最常用实体(entity)的简要描述,并附带它们如何使用的小示例。 在库与外部世界交互的每个点,请指定配置环境所需的步骤,以及如何验证这些步骤已成功完成。 如果不需要任何步骤,请显式声明。

如果可能,为你的库的每个支持版本提供单独的文档版本。 这可以防止用户查看过时或过于新的信息。 如果不可能,请清楚地标记文档中与已重新设计的 API 部分相关的章节。

为你的用户创建角色模型

在不清楚预期受众的情况下创建和评估文档是具有挑战性的。 为将阅读你的文档的用户类型定义多个角色模型(personas)可能会有所帮助。

考虑用户的约束,例如他们需要操作的现有软件栈。 文档审阅者可以采纳这些角色模型,以使其结论更有意义。

当你缺乏关于用户的具体信息时,最好保持悲观。 例如,不要假设用户精通 Kotlin 的最新或最先进的特性。 让你的代码示例尽可能简单。

当因时间、预算限制或保密协议而无法咨询实际用户时,角色模型尤其有用。 随着时间的推移,当你对用户有了更好的了解时,请完善角色模型以更准确地匹配他们的需求。

尽可能通过示例进行文档编写

通过示例进行文档编写是向用户解释基本概念最具成本效益的方法之一。 尽可能提供简单明了的代码示例,以帮助解释或演示当前讨论的主题或概念。

KDoc 文档格式允许你在文档注释中使用 Markdown 内联标记。 在注释中使用内联代码片段来展示 API 的用法。 例如,请参见协程库测试 dispatcher 的源代码渲染后的文档

提供这样的示例可以避免编写冗长的关于预期输入、可能输出和失败模式的描述。 但是,每个示例的上下文需要清晰,其相关情况也应清晰。 简单地提供一个未加注释的示例程序文件夹并不能算作文档。

彻底文档化你的 API

每个受支持的 API 入口点都应使用 KDoc 进行文档编写。

Kotlin 的文档引擎 Dokka 默认只在其输出中包含公共声明(declaration)。正如在“简洁性”章节中讨论的, 你应该最小化你的公共 API,并移除你不希望用户访问的公共入口点。 如果有些 API 你无法通过控制可见性来向用户隐藏,可以使用 suppress directive 将它们从文档中省略。

入口点的描述应以清晰、高层次的函数(function)功能概述开始。 避免简单地用自然语言复述签名。

例如,不要说“接受一个 String 并返回一个 Connection”,而是可以说 “尝试连接到由输入字符串指定的数据库,如果成功则返回一个 Connection,否则抛出 ConnectionTimeoutException”。

指定每个输入的预期值和不同输入下的行为。 解释有效值的区间以及提供无效值时会发生什么。 例如,如果一个字符串输入应该是 URL,请描述当字符串为空、无效、使用不受支持的协议或指向不存在的位置时会发生什么。

文档化 API 入口点可能抛出的每个异常。在一般描述中讨论失败条件,并将异常部分留给详细信息。这能增强可读性并帮助读者集中注意力。相反,将这些信息有机地整合到一般描述中。尽可能提供用法示例也有助于用户理解如何正确使用 API。

我们建议学习技术写作以提高文档的清晰度和有效性。 可以考虑探索诸如 Google 的本课程(第一部分第二部分)之类的资源。

文档化 lambda 形参

当一个 API 入口点接受一个 lambda 表达式时,用户正在提供一些由你的库代表他们执行的功能(functionality)。 这至少需要在两个方面进行额外文档编写。

首先,文档化如果 lambda 抛出异常会发生什么。考虑解决以下问题:

  • 这会导致立即失败吗?lambda 会被重复调用吗?还是有回退行为?
  • 如果调用函数需要退出,它会重新抛出 lambda 抛出的异常还是不同的异常?
  • 如果异常不同,它会包含原始异常吗?

此外,除非函数(function)被声明为 inline,否则请文档化与并发相关的任何特殊行为。 确保涵盖以下内容:

  • lambda 会在与调用者相同的线程中调用吗?
  • 如果 lambda 不在与调用者相同的线程中调用,它会在哪个线程(或线程池)中调用?
  • lambda 的多个副本可以并行运行吗?
  • 哪些其他作业(job)可能正在使用该线程?
  • 用户可以为库指定一个线程使用吗?
  • 当调用多个 lambda 时,关于排序提供了哪些保证?

在文档中使用显式链接

API 入口点完全独立于库中其他功能(functionality)的情况非常罕见。 通常,调用必须按特定顺序进行,完成特定任务有多种选项, 并且执行相关任务的入口点以类似方式使用。 例如,像 formatparse 这样的函数(function)相互镜像。

使用 @see 标签或内部链接在文档中显式说明这些关系。 这通过使读者能够将信息分块,从而构建更好的集成式库心智图,从而帮助读者。

尽可能地自包含

在描述输入的有效性时,很容易简单地引用相关的标准,例如由 W3C、IEEE 或 Unicode Consortium 制定的标准。 虽然提供这些链接可能有用,但不应强迫读者查阅外部规范来发现基本信息, 例如空白字符集。

在任何可能的情况下,文档都应自包含。 它应提供足够的信息,让用户理解每个 API 入口点的典型用法。 你可以将用户引导至外部文档,以了解在常见用法中不会发生的边缘情况。

使用简洁英语

在创建文档时,使用简洁明了的英语非常重要。 这确保了你的内容对全球受众(包括那些第一语言不是英语的人)是可访问的。 避免使用复杂的词语、行话、拉丁短语或可能让读者感到困惑的习语。 相反,使用直截了当的语言和简洁的句子。

简洁英语还能使文档在需要时更容易翻译。 清晰、无歧义的文本降低了误解的风险,并提高了整体可读性。

下一步

如果你还没有,可以考虑查看以下页面: