ACM:软件开发人员应该学习的10件事情


本文10 部分提供了适用于软件开发人员的有关学习的研究结果,并讨论了它们的实际含义。这些信息可以帮助您自学、教授初级员工和招聘员工。

  • 人类记忆不像计算机内存,是更容易遗忘和修改的,但也可以通过连接知识来进行问题解决和深入理解。
  • 人类记忆由长期记忆和工作记忆组成,长期记忆功能上类似于计算机的磁盘存储,工作记忆则类似于CPU的寄存器,存储有限但可以通过组块化来扩大容量。
  • 专家和初学者在处理概念和问题时有着不同的方式,专家更注重抽象概念而初学者则更关注细节。
  • 学习的效果受到时间间隔和重复的影响,间隔练习可以提高学习效果。
  • 学习应该结合抽象和具体的例子,同时休息和睡眠也对学习效果有积极影响。

1.人类记忆不是由比特组成的
我们的记忆存储在相互关联的神经通路中。当我们试图记住某件事时,我们会激活神经元通路来访问目标信息。然而,激活并不包含在一种途径中。一些活化能传播到其他连接的路径,例如从热水管辐射的热量。这种扩散的激活使相关通路在数小时内做好激活的准备。

传播激活意味着相关但不精确的信息可能会与目标信息混淆,这意味着我们对信息的回忆可能不可靠。

传播激活也与基于洞察力的问题解决或“顿悟时刻”相关。由于通路会在数小时内保持准备状态,因此有时会离开一个问题而去解决另一个具有自身扩散激活的问题,从而导致两个不相关的区域在中间连接起来。

当两个以前不相关的领域连接起来时,就会出现创造性和独特的问题解决方案。这就是为什么散步、淋浴或以其他方式花时间远离问题可以帮助你摆脱解决问题的困境。

2.人类记忆由一个有限系统和一个无限系统组成
在学习新工具或技能时,了解任务所需的认知负荷或工作记忆容量非常重要。认知负荷有两部分:内在负荷和外在负荷。

内在负载是指完成任务本质上需要多少信息或块;除非改变任务,否则无法改变它。相反,无关的认知负荷是不必要的信息,但它是执行任务的一部分。

如果您正在实现数据库表结构,则使用带有表和属性的图表比使用简单的英语描述更容易。

当面临似乎超出个人能力的任务时,重要的是要认识到可以通过重新组织任务来改变这种情况。将问题分解成可以处理和分块的更小的部分,最终使人们能够解决复杂的问题。

3.专家识别,初学者推理
初学者和高手之间的一个关键区别是,高手以前就见过这一切。
卡尼曼(Kahneman)将认知分为 "系统 1 "和 "系统 2"(由此证明,并非只有开发人员才会在命名事物上费尽心思)。

  • 系统 1 速度快,由识别驱动,依赖于长期记忆中的模式识别;
  • 而系统 2 速度较慢,侧重于推理,需要在工作记忆中进行更多处理。

这就是所谓双过程理论的一般观点。

专家级开发人员可以通过记忆(通常是经验中的隐性记忆)程序代码中的常见模式来进行更高层次的推理,从而释放他们的认知能力。
其中一个例子就是编程中的 "设计模式",类似于之前讨论过的大块。专家可能会立即意识到某段代码正在执行排序算法,而初学者可能会逐行阅读,试图理解代码的工作原理,却不了解全局。

由此推论,初学者可以通过阅读和理解大量代码成为专家。专家会在头脑中建立一个模式库,让他们今后更容易阅读和编写代码。纯粹的命令式 C 代码可能只能部分适用于函数式 Haskell 代码,因此多看看各种编程范式会更有帮助。

总之,这种模式匹配是阅读和处理更多代码、更多类型代码能够提高编程能力的原因。

4.理解概念要从抽象到具体再到抽象
研究表明,专家与初学者处理概念的方式不同。

  • 专家使用通用和抽象的术语来寻找基本概念,不关注细节;
  • 而初学者则关注表面细节,很难将这些细节与大局联系起来。

这些差异不仅影响专家的推理方式,也影响他们的学习方式。

例如,在向初学 Python 的人解释变量函数时,专家可能会说这是一个可以接受不同数量参数的函数。初学者可能会关注细节,比如声明和调用函数的确切语法,并认为传递一个参数是一种特殊情况。专家在向他们解释概念时,可能更容易理解或预测细节。

当你学习一个新概念时,两种形式的解释都会让你受益:抽象的特征和带有例子的具体细节。

您将受益于澳大利亚科学家卡尔-马顿(Karl Maton)定义的语义波。
根据语义波,您可以在抽象定义和该概念的多个不同示例之间不断切换。例子越多越好。即使是错误的示例,如果与正确的示例进行比较,也有助于理解它们为什么是错误的,例如,在试图学习什么是常量时,看到一个可变变量被标记为非常量。这一过程称为解包。

有了这些不同的示例,您就可以(重新)访问抽象定义,并构建对概念的深入理解。更深入的理解源于认识到实例中的多个细节是如何与定义中的一个抽象概念相联系的,这一过程称为重新打包。

编程经常涉及抽象概念的学习。面对要学习的抽象概念(如函数),人们通常会去寻找概念的具体实例来研究,例如返回数字绝对值的 abs 函数17。一个挑战是,随着概念变得越来越抽象(从值到变量/对象到函数/类到高阶函数/元类,最终到范畴理论),与具体实例的距离也越来越远。

值得庆幸的是,当我们学习抽象概念时,它们对我们来说会变得更加具体。

最初,函数是一个抽象概念,但经过大量练习后,函数对我们来说就变成了一个具体的项目(或块),我们就可以学习下一级的抽象概念了。

5.间隔和重复很重要
你听说过多少次考试前不应该补习?当然,除非你想在第二天忘掉一切。10 根据间距效应,人类学习问题解决概念的最佳方式是通过多次、多天、最好是多周的间隔练习。

6.互联网并未使学习过时
随着互联网的出现,编程知识的可用性发生了变化。有关语法或 API 的知识从埋没在参考书中变成了敲几下键盘就能获得。最近,ChatGPT、Codex 和 GitHub Copilot 等由人工智能驱动的工具甚至可以为你填补这些细节(大部分都很准确)。这就提出了一个显而易见的问题:如果在几秒钟内就能从互联网上获得相关知识,为什么还值得学习细节或任何知识呢?

互联网搜索需要大脑进行上下文切换;大脑有限的注意力和工作记忆必须从手头的任务(编程)切换到新的认知任务(搜索互联网并选择结果或评估人工智能生成的结果)。如果将所需的知识记忆下来,那么不仅访问速度会大大加快(就像使用缓存而不是从硬盘获取信息一样),而且还能避免上下文切换和过滤搜索中无关信息的认知消耗。因此,尽管可以在互联网上获取信息,我们仍有多种理由去记忆这些信息。

7.解决问题不是通用技能
解决问题是编程的重要组成部分。软件开发中一个常见(但不正确)的想法是直接将解决问题作为一种特定技能来教授,然后将其应用于开发的不同方面(设计、调试等)。因此,解决问题被(错误地)视为一种通用技能。然而,问题解决在大脑中并非如此。

虽然人类确实拥有一些通用的问题解决技能,但它们的效率远远低于特定领域的问题解决技能,例如调试程序的能力。虽然我们可以学习推理,但我们并不学习如何解决一般问题。

相反,我们学习的是如何解决编程问题,或如何规划国际象棋的最佳走法,或如何创建编织图案。这些技能各自独立,不会影响其他技能。对国际象棋的研究发现,学习国际象棋对其他学术和认知技能的影响很小,甚至没有影响,音乐教学和认知训练也是如此。这就是为什么 "大脑训练 "对开发一般智力无效的原因。

但空间技能似乎是个例外。空间技能使我们能够在头脑中想象出物体,如俄罗斯方块形状,并在头脑中操纵这些物体,如旋转俄罗斯方块形状。训练这些通用技能可以提高其他学科的学习能力。

最近的研究甚至表明,空间训练可以提高专业软件开发人员的效率,这很可能是因为他们仍在学习新概念。即使有这种奇怪的例外情况,学习如何解决编程问题的最佳方法仍然是练习解决编程问题,而不是从学习国际象棋或其他认知训练中寻找成绩上的益处。

这对招聘工作也有间接的影响。筛选编程应聘者的一个流行想法是出脑筋急转弯谜题,例如如何称量一架巨型喷气式飞机的重量。正如谷歌在 2013 年发现的那样,这是浪费时间--脑筋急转弯世界中的问题解决与编程世界中的问题解决之间没有可靠的对应关系。如果要评判编程能力,那就评估编程能力。

8.专业知识在某些情况下可能会成为问题
我们已经讨论了许多专业知识对学习和绩效有益的方式。然而,成为专家也可能带来问题。

程序员会使用一些工具和辅助工具来提高效率,例如版本控制系统或集成开发环境。这些工具会对初学者和专家产生不同的影响。初学者可能会被专业工具中的大量选项所淹没(由于认知负荷增加),并可能从初学者友好的工具使用提示中受益。

然而,专家会发现同样的提示更容易分散注意力,而不是更有用,因为他们已经知道该怎么做了。这就是所谓的 "专业知识反向效应":帮助初学者的提示和指南会妨碍专家的工作,降低他们的工作效率。

程序员在职业生涯中通常会学习多种编程语言。
一旦掌握了多种语言,就会受益匪浅,但有时将知识从一种编程语言转移到另一种编程语言可能会导致错误的知识。

专家经常帮助培训初学者,但没有培训经验的专家往往意识不到初学者的思维方式与专家不同。

专家经常帮助培训初学者,但没有培训他人经验的专家往往意识不到初学者的思维模式与他们不同,因此,他们无法针对思维模式不同的人进行有针对性的讲解。

这就是所谓的专家盲点问题:一旦成为专家,就很难用初学者的眼光看待问题。

要克服这个问题,可以认真倾听初学者解释他们当前的理解,并据此调整解释。

9.编程能力的预测因素尚不明确
与大多数活动一样,学习编程的成功与否取决于先天资质和后天练习。有些人认为编程纯粹与天赋有关--即 "天生我才必有用 "的观点;有些人则认为编程几乎完全与练习有关--即 "一万小时 "的观点,认为只有充分的练习才能获得专业技能。这两种极端的观点都是错误的,在本节中,我们将探讨能力和实践的不同影响的证据。

有很多研究试图预测编程能力,但几乎没有可靠的结果。对编程能力进行预测性测试的尝试一般都无果而终。研究发现,以下所有因素都无法预测编程能力:性别、年龄、学术专业、种族、以前的数学成绩、以前使用另一种编程语言的经验、对 CS 的看法以及对人文学科或科学的偏好。

简而言之,很难预测谁会编程,尤其是长期而言。程序员可能来自任何背景或人群,与任何其他因素(如智力)的联系在经验面前通常都是转瞬即逝的。因此,在招聘新程序员时,没有任何捷径可以识别编程能力,也没有任何可靠的 "候选人档案 "可以筛选出具有编程能力的候选人。

10.心态很重要
长期以来,人们一直认为编程能力是二元对立的:要么会编程,要么不会。

这背后有许多相互竞争的理论。其中一个较有说服力的理论是 "学习边缘动量 "理论 ,即每个主题都依赖于之前的主题,因此一旦落后,你将很难赶上。另一种不那么引人注目的理论是 "极客基因"(无论你是否天生如此)的观点,但这种观点几乎没有实证证据。

总之
我们将建议分为招聘方面的建议和培训与学习方面的建议。

在招聘方面,我们提出以下建议:

  • 编程能力没有很好的代用指标。基于性别、种族或其他因素的刻板印象没有证据支持。如果你想知道应聘者的编程能力如何,可以看看他们以前的工作或对他们进行真实的编程任务测试。强调一点:不要用脑筋急转弯谜题来测试应聘者。
  • 至少对于年轻的开发人员来说,工作年限可能不是衡量能力的可靠标准。
  • Behroozi 等人3 提出的一个相关建议是,让应聘者先在房间里独自解决面试问题,然后再展示解决方案,因为在解决问题的过程中,面试官会观察或要求交谈,这会增加认知负荷和压力,从而影响表现。

在学习和培训方面,我们提出以下建议:

  • 阅读大量代码有助于提高程序员的效率。
  • 专家不一定是培训初学者的最佳人选。
  • 学习需要时间,包括两次学习之间的间隔时间。密集的填鸭式学习没有效果,但有间隔的重复学习却很有效。
  • 同样,花时间远离问题也有助于解决问题。
  • 你可以通过互联网搜索或人工智能生成工具找到问题,但这并不意味着学习已经过时。
  • 在抽象概念和具体的可学习事实之间使用实例。
  • 追求成功(而不是避免失败),相信能力是可以改变的,这些都是抗挫折能力和学习能力的重要因素。