“大家好!”

API 文档编写编码人员指南

Peter Gruenbaum

您是否遇到过经理要求您为所开发的 API 编写文档的情况? 如果您像大多数开发人员一样,只喜欢写代码而痛恨写文档,就让我们面对这项挑战吧。 此外,写文档还要占用完成重要任务所需的时间,例如功能开发和错误修复。

API 文档常常以让读者失望和困惑而告终,这并不让人惊奇,因为它很少能得到应有的重视。

本文将指导您编写 API 文档。 我将介绍 API 文档中最重要的元素,并就如何创建有效文档提供一些建议。 我还会给您一些提示,告诉您如何创建优秀的概述、示例代码和参考资料,包括应当如何运用您的时间和精力来获得最佳效果。

为什么要为您的 API 编写文档?

让我们从非技术层面开始探讨这个问题。 自从第一个编程语言创建以来,API 文档就已经存在了。 尽管投入了大量时间来开发有效的流程,以便制作高质量的文档,但是真正写得好的 API 文档还是凤毛麟角。 为什么会发生这种情况?

首先,文档很少进行优先级划分。 尽管文档在软件平台的推广方面发挥了巨大作用,它的实际影响力还是难以估量。 因此,文档编写工作很难获得充足的时间和预算。 开发人员接到的文档编写工作通常都被列为最重要的任务,因此他们必须设法将其挤进已排满的工作日程当中。

其次,开发代码和编写文档是两种不同的技能。 有时候,开发人员需要使用母语以外的语言编写文档。 但是,即使他们出生在说英语的地区,被要求编写英文文档,也很可能有这样的情况存在:在学校的时候勉强通过文学和社会研究课程考试,而更愿意把时间花在解决数学和科学问题上。

制作优秀的 API 文档的第一步,就是向管理层要求足够的时间和预算。 有两点需要向经理说明:

  1. 优秀的文档能够使平台更容易推广,因为它可以减少开发人员的挫折体验。
  2. 优秀的文档还可以减少支持成本,因为开发人员更容易找到问题的答案。

如果您不喜欢编写文档,或者是您已经超负荷工作了,那么讨论优秀文档的重要性对您来说可能是一个挑战。但是,还有另外的选择。 如果有足够的预算,您可以雇用一位技术撰稿人,让他/她从您那里收集信息,然后编写文档。

正如开发人员一样,您也可以找到拥有丰富的经验和专业知识的技术撰稿人。 许多技术撰稿人在编写最终用户文档和支持资料方面拥有更为丰富的经验。 不过,对于 API 文档,您需要寻找具有实际软件开发经验的技术撰稿人。 许多公司将这类撰稿人列为类似于程序员/文档专家的职位。

拥有一些编码经验的技术撰稿人理解开发人员在竭力让软件平台运行起来的过程中所承受的痛苦,而优秀的文档能够帮助改善平台的开发过程。 此外,他们还应当在语言、算法和模式方面具备足够的专业知识,才能读懂您的代码,理解您的库。 有了这些知识和经验,撰稿人和开发团队之间的技术讨论将会更直接有效。

话虽如此,如果没有足够的预算来雇用一位技术撰稿人,您就需要自己编写文档了。 务必让管理层理解您需要挤出时间来做这项工作,就像您需要时间开发新功能一样。

API 文档的元素

一份优秀的 API 文档包含四种元素:

  1. 概述: 解释开发人员使用该平台有哪些优势,在有些情况下,还要提供平台的体系结构描述。
  2. 入门: 以分步教程或更简单的演练形式,帮助开发人员入门。
  3. 示例代码: 提供包含详细注释且能够让开发人员在此基础上继续开发的示例代码。
  4. 参考资料: 提供有关每个类、成员、函数或 XML 元素的详细信息。

当开发人员第一次开始阅读 API 的文档时,他们首先需要获得的信息是:谁需要使用这个 API,以及为什么要使用这个 API。 如果开发人员不了解这一点,他们很快就会转向别的工具。 遗憾的是,此信息常常被人遗忘。 对于 API 的开发人员来说,这一点显而易见,但是对于其他人却并非如此。

列举一些明显的示例,介绍您何时需要使用这个 API。 如果您已经有了一些客户,或一些潜在客户,则可以将他们当作真实的示例。 列举该软件平台的优势,最好能与现有的平台进行对比。 您会发现,项目经理通常拥有此类信息。

“概述”也是一个用来解释 API 整体体系结构的理想场所。 对于某些类型的 API(例如,许多 Web API)而言,API 本身已经足够简单,无需再进行体系结构讨论。 但是,如果您正在为某些包含许多类和继承结构的复杂 API 编写文档,那么提供全面的体系结构讨论并伴以图表,则可以帮助开发人员更好地理解 API。

入门

一旦开发人员决定试用您的 API,他们首先想知道的就是如何开始。 在 2009 年,我的公司 (SDK Bridge LLC) 针对文档进行了一项调查,调查结果中最普遍的反响之一,就是开发人员希望获得入门帮助。 (请参阅我的文章“Survey on SDK Documentation”,网址为 tinyurl.com/35l66yk。)这一点对于 API 的推广至关重要:如果开发人员发现很难入门,他们会很快放弃,然后寻找其他的途径来达成目标。

教程是帮助开发人员入门的一种好方法。 这种方法通常比说明性文字或体系结构图表都有效得多。 教程引导开发人员一步一步创建简单的应用程序,演示 API 的工作原理。 教程通常是从非编程的活动开始的,例如设置您的开发环境或获得授权凭据。 然后,它会引导开发人员逐步添加代码,直到代码能够演示通过 API 实现的简单任务。 教程的结构应该尽可能让开发人员很快获得一些代码,并且运行之后能够看到结果。 然后继续编写教程,添加更多功能。

因为您太熟悉 API 了,您可能已经忘记,如果从一个全新的视角来看,您的 API 会是什么样的。 因此,在编写这部分内容时,尽量退后一步,从一个新人的角度来思考。

编写示例代码

在 SDK Bridge 的调查中,另一个普遍的调查结果是:优秀的示例代码非常重要。 开发人员在学习新的平台时,会先以他们确信能够正常运行的代码开始,然后在其基础上修改或添加内容。 及时不是大部分,至少也有很多开发人员发现,实际动手要比只读书要更容易学会。

您可能知道如何创建优秀的生产代码。 优秀的示例代码与优秀的生产代码有很多共同点,但是也有几处截然不同。 通常,优秀的示例代码应当遵循以下原则:

  1. 相关的信息应当组织在一起。
  2. 清晰性比效率和可靠性更重要。
  3. 简单性比美观的 UI 更重要。

您可以将这些原则应用到特定的软件领域,看看示例代码与生产代码有什么不同。

每个程序员都知道,在代码中决不应当使用硬编码的值。 应当将这些值转换为常量,放在容易发现的地方,以便进行更改。

事实证明,这一原则对于生产代码适用,对于示例代码却不适用。 在示例代码中,您应当使用硬编码的值,以便将所有相关的信息尽量紧密地组织在一起。 如果您按照编写生产代码的良好做法,在文件的开头定义了所有常量,那么当开发人员看到使用常量的代码行时,就不得不滚动到文件开头去查看常量的值是什么。 这个简单的操作可能会打断他们的思路。 字符串、整数、十六进制值和其他简单值应当在使用它们的地方进行硬编码。

注释对于生产代码和示例代码都很有用,而在示例代码中,它们尤为重要。 每个类、成员或函数的前面都至少要有一行注释,解释它是什么以及它有什么功能。 无论何时,只要代码不是很浅显易懂,就应当使用注释进行说明,特别是在您记录解决方法或某些不常用的内容时。 如果需要,这些注释可以长达数行。 请使用完整的句子,不要担心过于罗嗦。

通常,每 5 行或 10 行代码应当至少有一行注释。 但这一原则有几种例外情况。 在您的演示中,完成外围功能的代码不需要这么多注释(例如,显示 API 结果所需的 UI 代码)。 如果您编写的是一小段代码,只包含了参考资料中的几行代码,可能根本就不需要注释。 如果您要提供一个非常大的、更像是生产代码的示例,则减少注释行的数量可能会更切实可行。

无论您编写的是生产代码还是示例代码,变量、类、成员和函数的名称都应当清楚明白。 而与生产代码相比,在清晰性比效率更重要的示例代码中更需要严格贯彻这一原则。 长而复杂的名称在生产代码中可能是个问题,但是在示例代码中就很值得,因为它们更清晰易懂。 即便是最短的变量名称也应该有意义;无论您多么希望,也不要使用没有意义的变量名称,例如“foo”或只包含一个字母的名称。

面向对象编程是软件工程最棒的发明之一。 您可能会很惊讶,尽管面向对象编程非常适合生产代码,但通常并不适合示例代码。 原因是,面向对象的设计会分散功能,以便将数据和函数组合到一起,它还会使用继承来减少重复代码。 请记住,优秀的示例代码的一个基本原则就是将相关的信息组织在一起。 而面向对象的代码则倾向于将相关的信息分散到不同的类中。 这样,开发人员就不必在继承层次结构中搜寻某个方法的用途,从而避免浪费时间,也不会打断思路。

当然,也有一些例外。 有些 API 需要面向对象编程才能正常运行。 非常大的、更像生产应用程序的示例可能也需要使用面向对象的设计。 但请注意,只要有可能,用户总是希望在一个类中看到所有必要的信息。

优秀的软件设计的一项基本原则是将功能封装到函数和方法中。 对于生产代码,这提高了清晰性并减少了重复代码。 这个规则也适用于示例代码,因为它通常可以创建代码块,开发者可以轻松将代码块复制并粘贴到自己的代码中,使用起来非常方便。

示例代码偶尔也需要大量的代码行,这些代码行与您的 API 没有直接关系,但对于示例的运行却是必不可少的。 在这种情况下,最好的办法就是尝试将这些不相关的代码行封装到一个函数或方法中,这样开发人员就能更轻松地略过这些代码。

除非您的 API 明确提供了您需要演示的 UI 功能,否则您应当让示例代码中的 UI 元素尽可能保持简单。 UI 代码会占用大量空间,并稀释您要演示的重要代码行。 开发人员并不在意您的示例看起来是否漂亮,他们只想了解 API 的工作原理。

如果您非得包含大量 UI 代码行,请将这些代码行包装到单独的函数中,使开发人员能够轻松阅读或略过。

最后,尽管异常处理对于生产代码的正常运行至关重要,但在示例代码中却会稀释相关代码,让读者分心。 通常,一种好的解决办法是不包含异常处理,而是加入一条注释,指明在生产代码中需要处理哪些异常。 但是,总会有一些情况,需要执行特定的调用以进行异常处理,在这样的情况中,用额外的代码行来展示异常处理的确切工作原理,还是值得的。

图 1 显示了示例代码中的一个函数示例,该示例代码演示了如何在一个社交网站中以 C# 发出 REST 请求,以便返回与指定用户相关的用户 ID。 在生产代码中,REST 端点 URL 应当作为常量与其他相关 URL 存储在一起。 然而在示例代码中,最好将此信息放在开发人员最希望看到它的地方,并在函数中建立与其角色的连接。 还需要注意,虽然建议使用错误处理,但本示例中并未采用。 为了简便起见,本示例中删除了 XML 处理。

图 1 示例代码示例

/// <summary> /// Returns an array of user IDs for users that /// are connected to the specified user.
Note that /// this is a simple, synchronous way to obtain the data.
/// </summary> /// <param name="userId">The ID of the specified user.</param> /// <returns>An array of user IDs that are connected to /// the specified user.</returns>

public int[] GetConnectedUserIds(int userId) { // Create variable to hold the returned IDs int[] connectedIds;

  // Construct a URL using the userId and the authorization token string url = "http://webservices.contoso.com/users/connections?userid=" + userId.ToString() + "&token=" + authorizationToken;

  // Create the Web request using the url HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
 
  // Get the response using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) {

    // Read the response XML StreamReader reader = new StreamReader(response.GetResponseStream()); string xmlResponse = reader.ReadToEnd();

    // Process XML to extract user IDs for connected users // and responseStatus ...
if (responseStatus != "ok") { // Handle errors here ...
}

    reader.Close(); }

  return connectedIds; }

参考资料

参考资料通常占据了 API 文档的大部分篇幅。 对于每个类、成员、函数、XML 元素等等,都需要提供详细的信息来介绍它们的作用及使用方法。 参考资料至少要涵盖以下内容:

  • 简要说明
  • 所有参数和返回值的说明
  • 任何能够为开发人员提供帮助的重要注解

如果有更多的时间和预算,请添加以下信息:

  • 可能需要捕获的异常
  • 指向其他相关概述或参考主题的链接
  • 一段示例代码,最好来自您已经编写好的示例代码

优秀的参考文档的风格应当始终一致。 有时已经有了风格指导原则,但往往需要您自己将它们弄明白。 图 2 列举了一些编写简短说明的一般性指导原则。

例如,请参考图 3 中所示的用于 Microsoft .NET Framework 中 Button 类的说明。 此示例直接摘自 MSDN 上的 SDK 文档。

图 2 参考文档的风格

类型 指导原则 示例
以“Represents”(表示)这样的词开头 “Represents a user’s photo album.”(表示用户的相册。)
方法和函数 以动词开头

“Returns the number of contacts for the specified area.”(返回指定区域的联系人数量。)

“Pauses the video.”(暂停视频。)

属性 使用名词,或以“Gets”(获取)或“Gets and sets”(获取和设置)这样的动词开头

“The user’s tasks.”(用户的任务。)

“Gets and sets a collection of the user’s tasks.”(获取和设置用户的任务集。)

事件 采用“Raised when”(当……时引发)或“Occurs when”(当……时发生)这样的句式 “Raised when the response from server is received.”(当收到来自服务器的响应时引发。)
XML 元素 使用名词短语 “The city’s postal code.”(城市的邮政编码。)
布尔值 对于布尔属性,采用“Indicates whether”(指示……是否……)这样的句式;对于方法和函数返回的布尔值,采用“Returns whether”(返回……是否……)这样的句式

“Indicates whether the control is visible.”(指示控件是否可见。)

“Returns whether two regions intersect.”(返回两个区域是否相交。)

图 3 参考文档示例

类或成员 类型 说明
类描述 表示 Windows 按钮控件。
Button 构造函数 构造函数 初始化新的 Button 类实例。
Focus 方法 将输入焦点设置到控件。
Visible 属性 获取或设置一个值,用于指示是否显示控件及其所有子控件。
Click 事件 当控件被单击时发生。

Web API

在过去几年中,Web API 的数量快速增长,因此值得我们思考一下 Web API 与本地 API 的不同之处。 “软件即服务”正在成为一种普遍的业务模型,各个公司也迅速发现更多客户希望能够直接通过自己的系统使用他们提供的服务。 这意味着服务提供商需要提供一个公共 API,以供他们的客户调用。

(有关术语的说明:我使用“本地 API”这个词来描述在 Web 出现以前就存在的典型 API。 从技术上来说,这些 API 可以是远程过程调用,因而不是本地 API;而 Web API 可以在与客户机同属一台计算机的服务器上调用,因而是本地 API。 但是,在大多数情况下,使用 HTTP 等标准协议的 Web API 通过远程方式使用,而其他 API 则在本地使用。)

由于 Web API 是一种相对比较新的技术,因此还没有标准来规范其文档的编写。 Web API 文档的质量良莠不齐,有的结构清晰、内容完整,有的却只有 Wiki 上的一点点信息。 如果您打算编写 Web API 文档,就值得花时间去研究各个公司的 API 文档编写风格,以便寻找合适的模板。 例如,Twilio 是一个语音和消息传送应用程序平台,拥有非常棒的 REST 文档范例,其文档可在以下网址找到:twilio.com/docs。 希望随着时间的推移,行业内会逐渐采用少数几种有效的模板。

从某种意义上来说,Web API 文档比本地 API 文档更为重要,因为开发人员更难以通过试用 Web API 来弄清其工作原理。 开发人员可以进行的调用次数可能有限制(配额),他们的实验可能会影响实际的系统,或者是很难模拟特定的条件,例如服务器处于高负荷之下的状况。

正如前文所述,开发人员非常依赖示例代码。 Web API 的一个强大之处就是不受平台和语言的限制。 遗憾的是,这也意味着在创建示例代码时需要付出额外的努力。 您可能会发现自己需要使用 Python、Ruby、Java、C# 等语言来编写示例代码。 设法了解您的客户最常用的语言是什么,然后着重关注对他们最重要的语言。

Web API 最常用的两种技术是 SOAP 和 REST。 SOAP 拥有一个定义格式(Web 服务描述语言,或 WSDL),这对于参考文档来说是一个好的开始,而 REST 则没有。 HTTP 调用示例和 XML/JSON 文件示例在演示这两种技术的工作原理时都很有用,但是还不够。 示例之后应该跟有表格,描述每个元素及其数据格式。

例如,仅仅将参数描述为字符串,可能还远远不够。 有没有它不能处理的特殊字符? 它的长度有没有限制? 如果某个 XML 元素是日期,您应当指定日期的格式。 如果是时间,则需要指定时区。

此外,您还需要解释如何处理错误。 具体取决于您的 API 支持的格式。 如果您的 API 使用 HTTP 响应代码来标记错误,应当在文档中加以说明。 错误文档应当解释错误发生的原因以及解决问题的方法。

Web API 通常需要身份验证,这也需要在文档中详细说明。 如果开发人员需要 API 密钥,请务必提供如何获得密钥的分步说明。 此外,请勿忘记 Web API 是基于 HTTP 构建的,而 HTTP 是极其丰富的协议。 您可能需要将与 HTTP 相关的信息记录到文档中,例如缓存、内容类型和状态代码。

Web API 是新鲜事物,我们还在探索为其编写文档的最佳方式。 希望在未来几年内能够看到相关标准。

发布

到目前为止,我一直专注于内容,但是您还需要发布文档,以便让开发人员阅读。 通常,开发人员希望看到基于 Web 的超链接文档,而不是 PDF 这样的平面文件。 有几种方式可以将您的文档发布到 Web 上。

如果您的 API 很小,那么很简单,只需创建 HTML 文件就足够了。 使用 CSS 来设计外观,以配合您公司的网站。

Wiki 提供了一种结构,适用于更为复杂的 API。 通过 Wiki,您还可以轻松地逐步更新文档或向文档添加内容,而无需使用其他工具或服务器。 此外,Wiki 的团队协作功能还可以让整个团队(甚至您的用户)共同参与文档编写。 但是,草草拼凑一个 Wiki,或指望开发人员和用户来编写文档,并不是非常可行的 API 文档编写策略。

关于 API 文档编写,有几个免费的开源 Wiki 引擎可供选择,而且它们已经非常流行,例如基于 PHP 的 MediaWiki (mediawiki.org/wiki/MediaWiki) 和基于 PERL 的 TWiki (twiki.org)。

Madcap Flare(请访问 madcapsoftware.com/products/flare)和 Adobe RoboHelp(请访问 adobe.com/products/robohelp)等商业文档编写工具主要用于编写最终用户文档,但也可以轻松应用于 API 文档编写。 这些工具提供了一个简单的 UI 用于输入信息,其外观比 Wiki 更精美。 通过这些工具,可以使用相同的源生成 Web 文档和平面文件文档。

PBworks (pbworks.com) 和 MindTouch (mindtouch.com) 等在线协作服务也逐渐应用于 API 文档编写。 除了 Wiki 的协作功能以外,这些工具还提供了更多功能,例如托管、精细的访问控制和脚本编写等功能。 这些服务通常需要支付订阅费才能用于商业用途。

赶紧行动起来吧!

优秀的 API 文档对于您平台的推广至关重要,还能减少公司收到的支持电话的数量。 如果您能够说服经理雇用一位具备合适技能的技术撰稿人,那就再好不过了。 如果不能,请遵循本文中的指导原则。

您的文档应当包含概述、入门帮助、示例代码和参考资料。 在概述中,请务必解释为什么要使用您的平台。 将教程集中到一起,帮助开发人员快速入门。 示例代码应侧重于清晰和简洁,而不应始终遵循生产代码的编码原则。 参考资料应当内容翔实、风格一致。 有许多工具可以帮助您将文档发布到 Web 上。

现在就开始编写文档吧!

Peter Gruenbaum   起初是一位物理学家,后来从事软件开发,涉猎的技术领域包括 Tablet PC、增强现实、计算机辅助设计和手术模拟。 他成立了 SDK Bridge LLC,将技术和写作的爱好融为一体,主要负责撰写技术文档和讲授技术。

衷心感谢以下技术专家对本文的审阅:John Musser (ProgrammableWeb) 和 Eugene Osovetsky (WebServius)