二、干净代码的原则

在上一章中,我们讨论了一段代码最开始的目的:为用户解决问题。 我们讨论了同时迎合机器和人的困难。 我们提醒自己,编写代码的核心是沟通意图。

在这一章中,我们将从那些创建软件时必须考虑的基础中得出四个核心原则。 这些原则是可靠性、效率、可维护性和可用性。 一个好的软件可以说具备所有这些品质。 一个糟糕的软件可以说没有这些。 然而,至关重要的是,这些原则并不是规则。 相反,将它们视为可以查看代码的透镜是很有用的。 对于每个原则,我们将通过类比和 JavaScript 示例的混合来发现它的重要性。 读完本章后,您应该能够将这些原则应用到您的代码中。

具体来说,我们将涵盖以下原则:

  • 可靠性
  • 效率
  • 可维护性
  • 可用性

可靠性

可靠性是一个好的软件系统的核心基础。 如果没有可靠性,技术的用处就会迅速消失,让我们陷入一种没有它可能会更好的境地。 不可靠可能会破坏技术的整个目的。

然而,可靠性不仅仅是大型复杂软件系统的特征。 每一行代码都可以被构造为不可靠或可靠。 但这是什么意思呢? 可靠性,这个词,指的是可靠的质量。 编写人们可以依赖的代码意味着什么? 可靠性是正确的、稳定的和有弹性的品质

正确性

正确的代码是符合一系列期望和需求的代码。 如果我写一个函数来验证电子邮件地址,那么期望的是,该函数可以调用所有类型的电子邮件地址,并正确地确定其有效性或无效如下:

isValidEmail('someone@example.org');     // => true
isValidEmail('foo+bar_baz@example.org'); // => true
isValidEmail('john@thecompany.$$$');     // => false
isValidEmail('this is not an email');    // => false

要编写正确的代码,我们必须首先了解需求是什么。 需求是我们对代码行为方式的正式期望。 对于之前的电子邮件验证功能,我们可能有以下要求:

  • 当传递一个有效的电子邮件地址作为第一个参数时,函数将返回true
  • 否则函数将返回false

第一个要求是模棱两可的。 我们需要辨明一个有效的电子邮件地址意味着什么。 电子邮件地址的格式看似简单; 然而,实际上有许多边缘情况和奇怪的有效表现。 例如,根据 RFC 5322 规范,以下电子邮件地址在技术上是有效的:

  • admin@mailserver1
  • example@s.example
  • john..doe@example.org

要知道我们的功能是否应该与 RFC 规范完全一致,我们首先需要了解它的真实用例。 它是一个电子邮件客户端软件的实用程序,还是可能用于一个社交媒体网站的用户注册? 在后一种情况下,我们可能希望将更奇怪的电子邮件地址建立为无效的,类似于前面列出的那些。 我们甚至可能想要 ping 该域中的邮件服务器以确认其存在。 重点是要辨别出哪些确切的要求将为我们提供正确的含义。

Incidentally, composing your own email validation function is very ill-advised, as there are many edge cases that need to be taken into account. This highlights an important consideration in our quest for reliability; often, we can achieve the highest level of reliability by using existing tried-and-tested open source libraries and utilities. In Chapter 17, Other Peoples' Code, we discuss the process of selecting third-party code in detail, and what to look out for.

我们编码的需求应该总是直接来源于我们的代码将如何使用。 从用户和他们的问题开始是至关重要的; 由此,我们可以建立一组可以独立测试的清晰需求。 测试我们的代码是必要的,这样我们就可以向自己和涉众确认,我们的代码满足了所有不同的需求。

在前面的电子邮件地址验证示例中,一组良好的测试将包含许多电子邮件地址的变体,以确保所有边缘情况都得到充分考虑。 在第 13 章中,我们更详细地讨论了测试。 然而,就目前而言,只要简单地思考正确性的重要性以及我们如何建立和确认正确性就足够了:

  • 理解正在解决的问题以及用户想要什么
  • 细化您的需求,直到明确需要什么为止
  • 测试代码以验证其正确性

稳定

稳定是我们在所有技术中都渴望的特性。 没有稳定,事情变得不稳定; 我们变得不确定事情是否会随时崩溃。 稳定性最好地体现在现实世界的一项普通技术上。 比较这两座桥:

[Photos from Unsplash / by Zach Lezniewicz / by Jacalyn Beales]

从技术上讲,它们都是桥梁。 然而,其中一架之前已经损坏,只能用一块简单的木板固定。 你相信哪座桥能让一百人安全通过? 可能是右边的那个。 它牢固地固定在栏杆上,最重要的是,没有缝隙可以让你掉下去。

在代码中,我们可以说稳定性是关于在不同的有效输入和情况下持续的正确行为。 浏览器中的 JavaScript 尤其容易以这种方式失败。 它必须在多种条件下运行,在不同的硬件、操作系统、屏幕大小,而且通常在具有不同功能的浏览器中运行。

如果我们编写的代码强烈依赖于某些条件,那么当这些条件不存在时,它可能是笨拙和不可靠的。 例如,如果我们正在为一个 web 应用设计和实现布局,我们可能会忘记考虑和满足屏幕尺寸小于 1024 像素宽,导致以下混乱:

这是不稳定的一个例子。 当某个环境因素不同时,不能指望 web 应用提供正确的行为。 在移动设备使用量不断增加的情况下,屏幕尺寸小于 1024 像素是完全可能的,也是合理的; 这是我们 web 应用的一个绝对有效的用例,如果不能适应它,就会对用户依赖它的能力产生负面影响。

通过充分理解代码可能遇到的所有不同的有效输入和情况,可以获得稳定性。 与正确性类似,稳定性最好通过一组测试来确认,这些测试将您的代码暴露在所有输入和情况下。

弹性

适应力是关于避免失败。 稳定性主要与预期的输入有关,而弹性则与代码暴露于意外或非例程输入时发生的情况有关。 软件系统的弹性也被称为容错,有时也被称为冗余偶发。 从根本上说,这些都是为了同一个目标——最小化失败的影响。

对于生命依赖于正在进行的功能的关键系统,各种偶发事件通常被内置到系统中。 如果出现故障或故障,系统可以利用其偶发性隔离和容忍故障。

NASA 在为航天飞机建造飞行控制系统时,通过一组同步的冗余机器将弹性融入到系统中。 如果其中一个因意外情况或漏洞而失败,那么另一个就会接手。 在地球上,我们在医院里建立应急设施,配备备用发电机,一旦电网中断就能立即启动。 类似地,一些城市交通网络也从突发事件中受益,如在火车不运行的情况下,替代公共汽车服务。

这些大型而复杂的系统似乎与 JavaScript 世界相去不远。 但通常,在没有意识到的情况下,我们也在常规地思考并在代码库中实现弹性。 我们做到这一点的一种方法是优雅的退化。 当我们为浏览器环境编写 JavaScript 时,我们有一些关键的期望:

  • JavaScript 将正确地通过 HTTP 传递
  • 浏览器支持的 JavaScript 版本
  • JavaScript 不会被广告拦截器或其他插件拦截
  • 浏览器通常没有禁用 JavaScript

如果这些条件中的任何一个都不成立,那么用户可能会面临一个完全不可用的网站或 web 应用。 减轻这些担忧的方法是在构建时考虑到优雅的退化。 优雅降级涉及到应用降级到仍可使用的状态,即使在出现意外故障时仍然对用户有用。

优雅的退化通常用一个简单的自动扶梯来说明:

[Photo via Unsplash, taken by Teemu Laukkarinen]

当自动扶梯运行正常时,它将通过一组由强大的齿轮系统和电机驱动的移动金属台阶来运送人。 如果系统由于某种原因出现故障,自动扶梯就会保持静止,充当正常的楼梯。 因此,电梯可以说是有弹性的,因为即使发生意外故障,它们仍然可以使用。 用户仍然可以在自动扶梯上上下移动,不过,这段旅程可能需要更长时间。

在编写 JavaScript 时,我们可以通过检测我们所依赖的特性,并仅在它们可用时使用它们,从而在代码中构建弹性。 例如,我可能希望向用户播放 MP3 音频。 为了实现这一点,我将使用 HTML5 Audio 元素。 然而,在此之前,我将检测浏览器是否支持 MP3 音频。 如果没有,我可以通知用户,并将他们指向音频文本:

function detectAudioMP3Support() {
 const audio = document.createElement('audio');
 const canPlayMP3 = audio.canPlayType &&
 audio.canPlayType('audio/mpeg; codecs="mp3"')
 return canPlayMP3 === 'probably';
}

function playAudio() {
 if (detectAudioMP3Support()) {
 // Code to play the audio
 // ...
 } else {
 // Code to display link to transcript
 // ...
 }
}

前面的代码使用 HTMLMediaElement 的canPlayType方法来识别支持。 我们将其抽象为一个detectAudioMP3Support函数,然后调用该函数来决定是继续播放音频,还是显示音频的文本。 显示音频的文本是一种优雅的退化形式,因为它仍然允许用户从音频中获得一些实用工具,而不能播放它。

重要的是要注意,特性检测本身并不是优雅的降级。 如果我检测到 MP3 支持,但如果它不可用,就会无声地失败,那就不会有多大的效果。 然而,为我们的用户激活另一种途径——在本例中是读取转录本——是优雅的退化和对失败的恢复能力的完美例子。

There's something curious about building resilience into the software. By thinking about and accommodating potential unexpected failure states, we are, in effect, making those failure states expected. This makes our software more stable and more usable. Over time, what we once had to be resilient towards will now be an everyday part of our software's stability.

弹性是编写干净、可靠代码的关键部分。 从根本上说,我们编写代码是为了为用户解决问题。 如果我们的代码能够容忍和容纳边缘情况、不可预见的情况和不可预料的输入,那么它将更有效地实现这一目的。

效率

我们生活在一个稀缺的世界。 资源是有限的。 为了写出最好的代码,我们需要考虑到这种稀缺性。 所以,在设计和实施我们的想法时,我们应该着眼于效率。

在本节中,我们将探讨效率的不同方面,并通过示例将它们与 JavaScript 世界联系起来。 希望你能体会到,效率不仅仅意味着速度快,它还包括许多间接影响,从经济到生态的方方面面。

时间

我们一直意识到的一个关键短缺是时间。 时间是一种至关重要的资源,我们应该在适当考虑的情况下设法使用它。 在编程领域,我们应该寻求优化在任何给定任务上花费的时间或 CPU 周期。 这是为了适应我们的终端用户,因为他们自己的时间有限,但也要谨慎地使用有限和昂贵的硬件。

对于 JavaScript 中的几乎任何函数,都有编写效率更高或更低的方法。 以这个函数为例,它删除数组中的重复字符串:

function removeDuplicateStrings(array) {

  const outputArray = [];

  array.forEach((arrayItem, index) => {

    // Check if the same item exists ahead of where we are.
    // If it does, then don't add it to the output.
    if (array.indexOf(arrayItem, index + 1) === -1) {
      outputArray.push(arrayItem);
    }

  });

  return outputArray;
}

这段代码是可靠的,满足了需求,并且,对于大多数意图和目的来说,是完美的。 但这是在做不必要的工作。 在数组的每次迭代中,它都在重复整个数组,从当前索引开始,以发现在当前位置之前是否存在重复值。 这似乎是一种直观的方法,但却是一种浪费。

我们不需要提前检查整个输入数组,而只需检查现有的输出数组中的特定值。 如果输出数组已经包含该值,那么我们知道不需要再次添加它。 以下是我们稍微优化的if条件:

// Check if the same item exists already in the output array.
// If it doesn't, then we can add it:
if (outputArray.indexOf(arrayItem) === -1) {
  outputArray.push(arrayItem);
}

还有其他方法来优化它,这取决于有多少唯一的值,以及输入数组的大小。 例如,我们可以将找到的值作为键存储在对象中(采用HashMap方法),这在某些情况下会减少查找时间。

Be wary of making micro-optimizations to your code. They may not always be worth the cost. Instead, first measure your code's performance, then work on the real performance bottlenecks that exist.  

在一项任务上花费太长时间会对用户执行任务的能力产生重大影响。 在本章的后面,我们将讨论可用性的原则,但现在,重要的是要注意,与时间的效率不仅是原则上的重要; 这一点很重要,因为在规模上,这些效率方面的微小努力可以对可用性产生巨大的积极影响。

空间

空间是一种稀缺性,与事物的大小有关。 数据通过网络传输到机器上,暂时存储在 RAM 中,也可能以硬盘或固态硬盘(hdd、ssd)的形式永久存储。 作为效率的支持者,我们感兴趣的是只使用完成给定任务所需的空间,其中一部分是以最有效的方式使用可用空间,并且只在谨慎的情况下移动数据。

由于 JavaScript 语言及其通常构建的应用的高级特性,我们很少需要考虑临时 RAM 的使用或永久存储。 然而,JavaScript 已经在性能敏感的环境(如数据库基础设施和 HTTP 中间件)中获得了重要的地位,成为了一种合法的语言,所以这些问题在今天更加重要。

此外,浏览器和本机环境中的客户端应用的需求也急剧增加。 这些应用的复杂性意味着我们必须时刻保持警惕,考虑如何优化服务器、用户设备和日益分层的网络上的内存和带宽使用。 我们在 web 应用中吸收的带宽将对用户等待应用可用的时间产生直接影响。

Time to first render is a common metric that we are interested in when developing frontend of web applications. This can be optimized by being prudent with large resources and not blocking the initial load time with unnecessary resources.

时间空间效率是密不可分的,两者都直接影响着对方。 效率的首要主题是只做必要的事,避免浪费,节约可用的资源。

效率的影响

在空间和时间方面的效率是许多其他影响的原因,无论是在软件本身还是在更广阔的世界。 没有优化是孤立存在的。 在一个领域的节约总是会在其他领域产生连锁反应。 同样,任何不必要的成本往往会在未来产生瓶颈和问题。

这些影响不胜枚举,但在软件世界中最明显的一些可能包括以下几点:

  • 电力消耗的生态效应(例如,气候变化)
  • 必须使用慢速软件的认知负担(例如,分心和烦恼)
  • 用户设备的电池寿命以及他们选择优先处理的任务

无论我们所做的选择是为了提高效率还是为了满足其他要求,总是要考虑到这些选择的连锁反应是很重要的。 我们创造的一切都不存在于真空中。

可维护性

可维护性是对代码进行适当更改的便利性。 与机动车辆不同,代码通常不需要例行维护以避免生锈等问题,但它确实需要不时地进行修复。 对其功能的更改也是经常需要的,特别是在活跃的开发过程中。 我们开发的很多代码也被其他人积极地开发着。 这种共享所有权在很大程度上依赖于可维护性的原则。

使代码可维护不应该是一个次要的优先事项。 它和代码要满足的任何其他需求一样重要。 在第一章中,我们谈到了考虑用户的重要性。 如果不看到那些维护和修改我们代码的人也是我们的用户,那就太虚伪了。 他们希望运用我们所创造的一切去实现一个目标; 因此,他们是我们的用户。 因此,我们需要思考如何才能最好地满足他们的需求。

在下一节中,我们将探讨可维护性的两个方面:适应性和熟悉性。

适应性

可以说,最好的维护类型是不需要发生的。 适应性是指你的代码迎合和适应不同需求和环境的能力。 代码不可能是无限自适应的。 代码的本质在于它是为特定目的而设计的; 为用户解决特定的问题。 我们可以并且应该在代码中提供一定程度的配置,以满足不同的需求,但是我们不能预见所有的可能性。 最终,有新需求的人可能会出现,并对底层代码进行更改。

如果我们要创建一个 JavaScript 组件来显示图像(幻灯片),很明显,用户会想要配置显示的特定图像。 例如,我们也可以有一个配置选项来启用或禁用旋转木马的渐入渐出行为。 我们完整的配置选项集看起来像这样:

  • (Array) images:您希望在旋转木马中显示的图像 url
  • (Boolean) fadeEffectEnabled:图像之间是否褪色
  • (Number) imageTimeout(图像超时时间
  • (Boolean) cycleEnabled:是否重复放映

这些配置选项定义了组件的可适应程度。 通过使用这些选项,它可以以各种不同的方式使用。 如果用户希望它以这些选项无法实现的方式运行,那么他们可能希望通过修改底层代码来改变它的行为。

当需要对底层代码进行更改时,尽可能少的麻烦是很重要的。 易碎性和刚性是可能引起麻烦的两个有害特性:

  • 脆性是当尝试改变时脆性的特征。 如果为了修复 bug 或添加特性而改变了代码的一个区域,并且它影响了代码库中不同部分中几个看起来不相关的东西,那么我们可以说该代码是脆弱的。
  • 刚性是不易适应变化的特点。 如果一种行为需要改变,理想情况下,我们只需要在一个地方做出改变。 但是如果我们必须重写所有的代码来完成一个更改,那么我们可以说这个代码是rigid

脆弱性和刚性通常是较大代码库的症状,其中模块之间有许多相互依赖。 这就是为什么我们说模块化是如此重要。 模块化是指以一种减少代码路径交织的方式将关注点分离到不同的代码区域。

There are various principles and design patterns we can use to accomplish modularity. These are discussed in Chapter 4, SOLID and Other Principles, and, with more code examples, in Chapter 11Design Patterns. Even at this early stage, it is useful to ask yourself: in what ways can I accomplish modularity?

努力避免脆弱性和刚性是一个很好的目标,它将使我们的代码更适应更改,但是对于维护者来说,代码库最关键的方面是它的可理解性。 也就是说,它可以被理解的程度。 如果不了解什么,维护人员甚至不能开始进行更改。 事实上,在模糊和令人困惑的代码库中,有时甚至不可能识别是否需要更改。 这就是为什么我们现在要把熟悉作为可维护性的一个方面来探讨。 通过使用熟悉的约定和直观的模式,我们可以帮助确保维护人员之间的高度理解。

熟悉

熟悉是一种可爱的感觉。 这是一种舒适的感觉,你知道发生了什么,因为你以前见过。 这是我们应该希望灌输给所有可能遇到我们代码的维护人员的感觉。

想象一下,你是一个熟练的机械师。 你打开一辆旧车的引擎盖。 您希望所有不同的组件在它们各自的位置都是可见的。 您很擅长识别特定的组件,并且,即使不需要移动任何东西,您也可以看到组件是如何连接在一起的。

做了一些小的修改; 也许车主之前安装了涡轮增压引擎或修改了齿轮传动比,但总的来说,你会发现一切或多或少都在它应该在的地方。 对于你来说,做出改变的机制是非常简单的:

[Unsplash image (Public Domain) by Hosea Georgeson]

在本例中,引擎盖下的所有东西都在其预期和指定的位置。 尽管汽车在许多方面有所不同,但它们的基本功能是相同的,所以它们的布局和设计与机制是相似的。

当我们思考这个问题时,软件并没有那么不同。 我们最终创建的大多数软件在许多方面都与其他软件相似。 例如,大多数 web 应用都有用户注册、登录和更改名称的方法。 大多数软件,无论问题域,都会有创建、读取、更新和删除(CRUD)的概念。 这些构成了著名的持久存储动词。 大多数软件可以被认为是位于持久存储之上的奇特中间件。 因此,尽管我们可能认为所有的软件应用都是非常不同的,但它们的基本原理通常是非常相似的。 因此,编写符合机制(打开引擎盖)的代码应该不是那么困难。

为了让机制的工作尽可能简单,我们首先需要关注代码的熟悉程度。 做到这一点并不简单,因为不同的人熟悉不同的事物,但总的来说,我们可以从以下指南中吸取教训:

  • 不要偏离常见的设计模式太远
  • 与语法和表示保持一致
  • 使不熟悉的问题领域变得清晰

最后一点提到了不熟悉的问题域。 这是作为程序员的您在处理每个代码库时必须考虑的问题。 为了辨别某些内容是否被认为是不熟悉的,你可以问自己:在其他行业工作的程序员是否能够通过简单的介绍理解这些内容?

可用性

可维护性主要是为了迎合其他程序员,而可用性则是为了迎合所有用户,无论他们是谁。 我们可以说有两大用户群体在使用我们的服务:

  • 希望通过接口(gui、api 等)使用我们代码的人
  • 人们希望改变我们的代码来完成新的任务或修复 bug

可用性是指让我们的代码,以及它所支持的功能和交互,尽可能对所有用户都有用和容易使用。 所有代码在编写时都至少考虑到一个用例,因此,基于代码实现该目的的程度来判断代码是公平的。 然而,可用性远不止于此。 可用性不仅仅是满足用户需求; 它是关于创造能够让用户以最小的麻烦、时间和认知努力实现他们的目标的体验。

无论我们是在网络上创建用户界面,还是在深度嵌入的服务器基础架构上创建用户界面,可用性都是至关重要的。 在这两种情况下,我们都在为用户提供服务,因此我们必须关注可用性。

看看这个函数的签名,并试着辨别你将如何使用它:

function checkIsNewYear(
  configuration,
  filter,
  formatter,
  MDY,
  SMH
) {...}

这个函数是我曾经工作过的代码库中的一个真正的函数签名。 它没有文档,里面的代码像意大利面条一样。 它用于计算给定的时间是否可以被视为新年,并决定何时向用户显示Happy new year消息。 然而,如何使用它或它的工作原理却令人难以置信地不清楚。 一些开放的问题,我可能会发现这个函数如下:

  • 什么是配置,在这样一个简单的功能中可以配置什么?
  • SMH 大概是秒、分、小时,但是期望是什么值呢? 一个对象?
  • MDY 大概是个月,天,年,但它的期望值是什么呢? 一个对象?
  • 当识别是否为新年时,函数比较已经过的日期为哪一年?
  • 在名义上的新年中,是否有日期起作用,或者只有 1 月 1 日起作用?
  • 为什么有过滤器格式化器参数,它们做什么? 他们是可选的吗?
  • 函数返回什么? 一个布尔? 格式化器参数建议不使用。
  • 为什么我不能传递一个日期对象而不是单独的日期组件?

这个函数可以按要求执行,但是,如您所见,它不是很好用。 这需要大量的时间和认知努力来弄清楚它是如何工作的。 为了弄清楚它,我们必须研究它在代码的其他部分的用法,并试图破译其中的意面。 作为一个使用这个功能的用户,我个人觉得整个过程非常痛苦。

可用性就是要避免这种痛苦和负担。 作为程序员,我们从事于抽象的创建以简化复杂的任务,但是前面的所有代码实现的是一个简单问题的进一步复杂化。

用户故事

可用性是指某物在特定目的下易于使用的程度。 它的目的是通过一个易于理解的问题模型和一组明确的需求来定义的。 明确这些目的的一种有用的技术是通过用户故事,这是 Scrum 和敏捷方法所著名的。 用户故事通常采用以下形式:

As a {persona}, I want to {want}, so that {purpose}...

如果我们在设计一个Contacts应用,这里有一些用户故事类型的例子:

  • 作为一个用户,我想添加一个新的联系人,以便以后可以从我的联系人中回忆起那个联系人。
  • 作为一个用户,我想删除一个联系人,这样在我的联系人列表中就不会再看到那个联系人了。
  • 作为一个用户,我希望能轻松找到一个姓的联系人,这样就能联系到

用户故事有助于定义您所迎合的目的,并有助于将注意力集中在用户的视角上。 无论您创建的是 5 行函数还是 1 万行系统,规划用户故事总是值得的。

直观的设计

直观的设计就是让用户不需要投入认知的努力去弄清楚某些东西是如何工作的。 直观设计的核心思想是,它只是工作

当我们编写代码时,我们参与了它的设计、宏伟的架构、功能和逐行语法。 所有这些都是设计的重要部分。 使用直观的设计模式对于编写可用的代码至关重要。 所有用户都被调整到一组在他们的抽象级别上使用的模式。 下面是一些例子:

  • :使用X按钮表示程序或进程退出
  • 在代码中:以开头的函数或方法为表示布尔返回值
  • :用绿色表示积极行动,用红色表示消极行动
  • 在代码中:大写常量,例如VARIABLE_NAME
  • :使用软盘图标表示保存的概念

这些是许多用户在使用软件时随身携带的假设和期望。 利用这些假设意味着您的代码及其所促进的交互可以非常容易地使用。

可访问性

易用性是可用性中的一个关键原则,它说明了满足所有用户的重要性,而不管他们的能力和环境如何。 可用性往往关注用户本身,就好像他们是一个单一的实体。 我们通常会对用户做出特定的假设,赋予他们一系列可能在现实中无法反映的特征和能力。 然而,可访问性是关于最终不得不使用您创建的任何内容的真正用户。 这些真正的用户是一组不同的个体,可能有各种各样的差异。 当我们谈论软件的可访问性时,我们通常关心的是直接影响一个人使用该软件的能力的不同类型。 这些可能包括以下内容:

  • 学习障碍或差异,如诵读困难。
  • 身体残疾。 例如,手的活动能力有限或失明。
  • 发育障碍,如自闭症和多动症。
  • 由于流动性、经济或基础设施的原因,获取技术的机会更少。

除此之外,还有许多跨越人类生存领域的差异,所以我们应该随时准备学习和适应我们在用户之间遇到的新需求和差异。

我们致力于在服务器和浏览器上创建 web 应用。 作为 JavaScript 程序员,我们非常关注服务于最终用户的接口。 因此,对网络的可访问性有一个很好的把握是至关重要的。 这包括对 W3C 发布的Web 内容可访问性指南(WCAG 2.0)的认识,其中包括以下条款:

  • 为任何非文本内容提供文本替代(准则 1.1)
  • 让所有功能都可以通过键盘使用(指导原则 2.1)
  • 使文本内容可读性强、易于理解(准则 3.1)

可访问性不仅仅是关于非程序员的最终用户。 如前所述,我们应该将其他程序员视为我们的用户,就像 gui 或其他 web 界面的终端用户一样。 迎合其他程序员是至关重要的。 有些程序员是盲目的或视力不全的。 一些程序员有学习或认知方面的困难。 不是所有的程序员都在最新最快的硬件上工作。 也不是所有的程序员都理解所有你认为理所当然的事情。 在编写代码时考虑所有这些因素是很重要的。

读完这一章后,你可能会有一种被大量的原则、原则和指导方针所压倒的感觉。 事情可能看起来很复杂,但如果我们遵循一个简单的规则——始终关注用户,事情就不会那么复杂。 另外,请记住,其他可能在您的代码上工作的程序员也是您的用户。

作为程序员,我们拥有前所未有的能力来帮助定义人们在执行各种任务时的行为。 在 Twitter、谷歌或微软工作的最初的程序员可能没有预见到他们的代码会运行多少次。 他们最初可能无法想象他们的代码最终会影响多少人。 我们应该始终对这种力量保持谦逊,并努力认真地对我们服务的所有用户以及他们寻求执行的所有无数任务负责。 如果你想从这一章学到什么,我希望它是简单的:在你写的每一行代码中,都要谦虚地和持续地考虑用户。

总结

在本章中,我们探讨了可靠性、效率、可维护性和可用性等重要原则。 使用这些原则作为透镜,我们可以通过它查看我们的代码库,确保我们将更有可能编写干净的代码。 我们在这一章学到的最重要的事情之一就是在我们编写的代码中始终考虑到人。 用户可能是坐在 GUI 另一边的人,也可能是使用我们的 api 的程序员。 无论如何,不断地意识到这个人是至关重要的。

在下一章中,我们将继续研究干净代码的基本特征,通过观察需要注意的敌人,如货物崇拜编程和自我。