命名是软件开发中最难的事情

admin 2023年2月20日21:32:13评论32 views字数 7136阅读23分47秒阅读模式

Phil Karlton 曾经说过:“计算机科学中只有两件难事:缓存失效和命名事物。”

一个是现实问题;另一个是“定义现实”的问题。 

失效的缓存算法会损害系统的完整性。不恰当的名称会危及系统的整体存在。

从表面上看,这似乎太过跳跃。然而,当你研究我们是如何学习以及建立对世界的共同理解时,它还是有意义的。

我们的知识来自对我们周围现实的体验和解释。通过软件,我们构建了虚拟世界。我指的不是元宇宙意义上的虚拟世界。我的意思是我们脑海中的虚拟世界。

我们建立新的虚拟世界是因为现实充满了一切。当在虚拟世界中呈现真实世界的体验时,构建过程会经历三个不同的阶段:

  1. 按原样复制体验或活动。

  2. 识别并删除对体验无益的无关步骤和任务

  3. 添加物理世界中没有的有用功能。

以网上购物为例。

这种在线体验融合了现实的许多方面,例如浏览商品和付款。其他概念,例如开车去商店,不会增加任何价值,也不包含在虚拟体验中。还有一些概念只在虚拟世界中有用,而且可能也只存在于虚拟世界,比如存储信用卡号以备将来购买。

当我们构建这些新现实时,我们必须首先发明事物,然后为这些事物命名。对现实的不明确、非常规或不充分的认识会导致名称混淆。

令人困惑的名称将难以分享这些名称下的知识。如果没有足够多的人对现实有相同的解释,系统总是会失败。

在讨论技术前我们先讨论些偏哲学的问题。


 1 
现实的语义


在理解为什么命名困难(且至关重要)之前,我们必须了解我们的大脑如何将周围的世界“翻译”成知识。

名称不仅仅是符号和声音的序列,它代表了心理学家称之为模式的概念。

模式对于软件开发来说可能有点抽象,所以我更喜欢使用一种特殊形式的模式,称为语义网络。

语义网络的最简单形式包含实体和关系:

实体代表不同的工件类型,无论是真实的还是概念上的。如果我们谈论树木,一些可识别的实体将是“树干”、“树枝”、“树叶”、“果实”、“花朵”和“种子”。

关系描述了两个实体之间的关联。例如,叶子“连接”到树枝,树“长出”果实。

命名是软件开发中最难的事情

语义网络(左侧)识别和连接实体。随着我们对一个主题的了解增多,语义网络变得更密集,新实体不断添加到网络中。当我们对某个主题了解更多时,我们的大脑也会重组这些网络。

虽然语义网络代表世界,但存在不同但同样有效的表示。有些表示在深度上不同(更详细地看世界),而另一些表示在视角上不同(从不同角度看世界)。

让我们从一个教别人水果起源的例子开始。

蹒跚学步的孩子询问水果的来源可能会对“树”、“树枝”、“花”和“水果”的简单语义网络感到满意。

在以后的生活中,也许作为一名生物学学生,同一个学生将准备好理解更复杂的模型。该模型将包含新的实体,例如“种子”、“花粉”、“花粉管”、“子房”和“花粉囊”。这些新实体将需要更多的语义网络关系,连接现有实体和新实体。例如,花粉“存在于”“花粉囊”中,而花粉囊是花的“一部分”。

这两个语义网络在深度上有所不同。

Apilogists——蜜蜂专家——检查树木时,在他们的语义网络中肯定有许多这样的概念。然而,语义网络可能包括不太常见的实体,例如与蜜蜂在生态系统中的生计和一般生存能力有关的“森林缓冲区”和“防风林”。这些语义网络仍然包含有关树木的额外信息,但仅当该信息与蜜蜂有关时。 

这就是视角的不同。

命名是软件开发中最难的事情

对一门学科的终身研究使不同的人对同一事物的看法不同。根据观察者的专业知识,一个简单的水果可能会唤起越来越复杂和不相关的概念。

这些差异在处理现实时引入了两个基本挑战,你可能会在软件开发中开始认识到这些挑战:

  1. 不同的技能水平意味着不同的语义网络深度。有些人比其他人能在客户场景中看到更多细微差别。这并不意味着额外的感知总是会对最终解决方案产生实质性影响,但它确实存在。

  2. 不同的优先级会影响我们如何对相同的感官信息进行分类。软件开发人员可能会在服务器、连接和呈现引擎的复杂物理和逻辑网络中识别支撑整个系统的实体。对用户来说,也许他们看到的只是“一个应用程序”,这是他们日常生活中的一个小实体。

我们或多或少熟悉深度上的差异。我们不太适应视角的差异。


 2 
一个全新的虚拟世界


在这一点上,你可以看出困难的部分不是命名某物这最后一步。

一个好名字的背后是对现实的清晰感知和语义网络的形成。概念和关系必须从观察中浮现出来,然后我们才能命名它们。

在有记载的历史的某个时刻,一定有人看着地上的一根高大的棍子,并称它为树——准确地说,他们称它为别的东西。

但是当某人的现实不遵守空间、物质和时间的规则时会发生什么?

这就是软件难题,强调“软”(虚拟)而不是“硬”(具体)。软件开发人员不会简单地为他们世界中的实体指定名称。通常,他们创建这些实体。

命名是软件开发中最难的事情
软件开发人员可能会以或多或少明显的方式向另一个观察者解释现实。一些概念可能很简单,并且可以在不同的开发人员之间共享。其他概念可能需要复杂的解释并在团队中形成新的语义网络,然后人们才能围绕它进行协作。

因此,软件开发人员比其他行业的人更频繁地面临命名决策。

举几个其他行业如何遇到命名新事物挑战的例子,我们必须看看他们遇到新事物的频率。

物理学家需要使用能够窥视亚原子粒子深处的仪器来剖析宇宙的基础。天文学家利用有限的资源四处张望,希望发现一个需要重新命名的天体。企业家必须先创建公司和新产品,然后才能命名。

结果清晰了:大量的工作,漫长的发现期,现实世界的限制,也许他们各自领域的顶尖人物每年(甚至十几年)才会获得一些命名机会。

软件开发人员呢?他们一天就有几十次,甚至可能几百次这样的机会。

机会来自处理能在程序中代表思想和概念的媒介。这些表示必须能够被其他程序严格且明确地引用。


 3 
知识转移,从理论到实践


一旦所有新事物都被命名,就该向开发团队的其他成员介绍这些新世界了。

由于大多数软件开发人员缺乏正式的教育背景,他们通常依赖于向他人学习和教授他人过程中产生的经验。

关于“学习理论[1]”的正式教学培训可能还很遥远,但我们至少可以略读某些方面。 

对学习理论的更深入分析值得单独发表一篇文章。尽管如此,简单了解其中一些概念,比如“学习迁移”,也显示出解释和改进传统软件设计共享方法的潜力。

值得注意的是,这些传统方法仍应是任何知识转移的基础:

  • 设计文档

  • 组件图

  • 术语词典

  • 交互图

请注意,白板会议是对设计文档的补充,而不是替代。

需要明确的是,现场会议是一种很好的认知工具。“学生”可以与“教师”互动以探究概念并逐渐增强他们的语义网络。

另一方面,培训的实施在很大程度上取决于教师的经验。个人交付导致跨会话的结果不一致。个性化交付的扩展性也很差,并且边缘化了社交网络、地域和时区边界之外的同事。


 4 
隐喻:小心“现成的”语义网络。


不同的人以不同的方式形成语义网络。在软件开发中,当你处理适合多个可能类别的抽象概念时尤其如此。

毫不奇怪,设计人员和开发人员时不时地喜欢使用隐喻来建模或描述他们的系统[2]。他们的想法是,隐喻可以通过建立在人们之前已经学习过的类似语义网络上来加快知识转移。

命名是软件开发中最难的事情


隐喻是新语义网络的固定版本。接收者可以“从头开始”构建一个新的语义网络,但可以从类似语义网络的预先存在的片段中构建得更快一些,如果不是更完美的话。

一个简单比喻的例子是使用“垃圾桶”图标从计算机中删除文件。隐喻的一个更详细的例子是消息系统中“队列”概念的增选。

但是,使用隐喻有(至少)两个问题。

隐喻问题 #1:隐喻被谁所熟知?

第一个问题是,熟悉的概念不一定具有普遍性。

一个完美的例子来自名为“Darmok”的星际迷航剧集。

这个例子很完美,因为《星际迷航》的粉丝们已经明白我的意思了,其他的就不用多说了。与此同时,非粉丝们想知道为什么这一集有意义。

在那一集中,一个外星种族通过根植于他们家乡历史事件的隐喻,在一个星球范围的内幕故事中进行交流。结果,由于缺乏共享背景,与任何其他社会的交流都受到阻碍,导致长期而徒劳的联系尝试。

附带一提,这个故事还展示了共享上下文的不可思议的力量,其中最少的语言提示可以快速传达密集的信息。

索卡斯,他的眼睛没有遮盖!

隐喻问题 #2:深度和视角。

我们再一次面对我们的语义网络恶棍。

如果我说某个东西“像互联网一样工作”,技术人员可能或多或少地了解互联网。同时,一些没有云基础设施背景的互联网高手可能会把它看成是串管。

即使在云基础设施工程师的技术社区中,有些人可能直到 2021 年 Facebook 大规模宕机后才听说过 BGP(边界网关协议)。而像我这样当时刚刚通过阅读文章了解 BGP 的人,可能已经有了将 BGP 添加到他们的语义网络中,作为[将大公司连接到公共互联网的协议]的一些松散版本。

学习理论也有证据表明,在不同经历中传递知识并不是一项通用技能:

学习迁移是指一个人在学校学到的东西会以某种方式延续到与特定时间和特定环境不同的情况……他发现尽管迁移对学习极其重要,但这种现象很少发生。

事实上,他进行了一项实验,让受试者估计特定形状的大小,然后他会切换形状。他发现先验信息对受试者没有帮助;相反,它阻碍了他们的学习。


 5 
抽象。它(几乎)一直是抽象。


可以把抽象理解为将整个语义网络分组为新语义网络中更少的实体。

抽象可以实现更高层次的通信,无论是人与人之间还是人与机器之间。

作为现实世界抽象的一个例子,想想在线购物,其中购买体验在计算机屏幕上用图像和按钮表示。

命名是软件开发中最难的事情

观察现实的系统设计者必须就现实的哪些部分在新现实(新系统)中必不可少做出明智的决定。那个新现实是对原始现实的抽象。

虽然抽象并不是软件所独有的,但软件行业的独特之处在于它以多快的速度将抽象堆叠在抽象之上。这种堆叠会产生难以理解的语义网络层,需要陡峭的学习曲线。

为了说明这些软件抽象堆栈有多“高”,让我们尝试向外行解释 Kubernetes 集群:

Kubernetes 集群将各种 Kubernetes 节点组合成一个逻辑计算单元,其中包含这些节点的聚合计算能力。Kubernetes 节点是计算能力的抽象,它可以是运行在虚拟机管理程序上的虚拟机、实际的裸机服务器,甚至是桌面上的一小块 Raspberry Pi 机架。 

你可以在 Kubernetes 节点上运行程序。但首先,你需要了解“pods”的抽象概念,即部署在一起的“容器”组。

解释可以继续,直到它不可避免地压倒了一个人扩展其语义网络的能力。

这里的要点是,更简单并不意味着简单。有些技术本身就很复杂,因为它们解决的是复杂的问题。

因为我们需要就这些问题的解决方案进行有效的推理和沟通,所以我们一直在抽象之上层层抽象,直到解释看起来很像现在著名的“一直往下全是乌龟[3]”的故事。

软件开发人员,或者至少是软件架构师,最好略读一下认知负荷[4]的概念——应用于软件设计的认知负荷主题是未来帖子的主题。


 6 
从无处不在到独一无二


随着我们的学习,语义网络的数量和规模都在增长。它们也像树一样生长,新概念附加到现有概念上。我们可以在大脑中重组部分语义树,但完全“重写”并不容易或不常见。

两个个体的语义网络可能会更靠近它们的语义核心,尤其是当这两个个体共享很多上下文时。

随着抽象级别的提高,共享上下文将减弱,并且这些语义网络在边缘变得越来越不同。当这些网络变得足够不同时,这些人就不再有相似的观点。

作为软件开发中的热身练习,让我们看一下在函数内命名变量的狭窄范围。函数内的语义网络在用途上非常有限,程序员试图使名称适合局部控制循环或算法的一部分。

循环控制需要命名索引变量或枚举元素。这些命名决定在很大程度上是衍生的和无关紧要的。例如,当涉及到索引变量时,默认决策通常遵循单字符名称的历史模式,如“i”,“j”和“k”。

命名是软件开发中最难的事情


两个不同的开发人员接受过类似的培训,面对一个简单任务的相同抽象定义,可能会得出看起来非常相似的解决方案。当相同的开发人员处理更高级别的抽象时,该协议可能会消失。

通过循环控制,我们正在处理具有更高语义目的的命名实体。在这一点上,我们可能试图使名称符合包含变量的函数的目的。

随着命名练习在概念层次结构中向上移动,我们开始将名称与系统概念对齐。例如,如果系统有一个代表购物车的组件,我们可能会有一个文件或系统类的命名方式类似于购物车。

这里重要的是不同的人为同一个现实创建不同的语义网络。当这个现实非常狭窄时,例如“循环遍历这个数组”,语义网络将同样受到限制并且往往会完美对齐。

随着范围逐渐扩大(控制流、将算法建模为程序或定义系统),与该范围直接相关的一致性会逐渐减弱。

当我们从客户如何看待系统这一心智模型来对齐时,对齐一词无法弥补差距。对于客户来说,构成我们的世界和专业知识中心的可能只是“来自 6 号供应商的 3 号应用程序”。

回到我们的购物车示例,两个不同的程序员会将购物车函数表示为相同的源代码文件吗?可能不会。

更重要的是,他们会同意别人的决定并调整自己的思维模式吗?可能吧。

协议意味着在团队中不同的人之间“对齐”语义网络,并且有一些很好的技术。前面提到了一些形式化方法(设计文档、术语词典、组件图、序列图。)

我会添加频繁的交叉教育甚至结对编程作为可以补充正式文档的活动。

讨论在开发团队中增加“语义一致性”的方法——例如业务分析、领域驱动设计或 RUP——是未来发布的另一个主题。我有意将这些过程留在讨论之外。这些流程需要创建现实的业务和领域模型,使我们回到本文中已经解决的相同挑战。

区分语义网络将把我们带到下一节。


 7 
通过代码重写现实


重做某人的工作是软件行业中独特的常见做法。

乍一看,返工是一种浪费的活动。从表面上看,也许从项目经理的角度来看,如果一段代码正在完成它的工作,开发人员应该不要管它。如果代码块需要最少的修改来添加新功能,那么用增量来扩充该组件并保持其余部分不变应该更有意义。是这样吗?

这里的脱节在于,项目经理考虑构建东西。本能地或通过培训,项目经理期望当你在构建现实世界中的某些东西时,它就完成了。

然而,再次强调,软件开发人员并不是在现实世界中构建东西;他们正在解释现实以创建虚拟世界。

抛开代码重构的正当理由不谈,软件开发人员从另一个开发人员“继承”代码库可能仍然会感到重写该代码库的重要部分的冲动。

这是为什么?

一句话:建构主义[5]。

简而言之,系统中预先存在的代码不是静态工件。它是对现实的解释。在虚拟世界中,它则是不断变化的现实。在这种情况下,几乎不可能在整个团队中达成对现实的共同的、稳定的理解。

重写代码并不是重建代码仓库中已有内容,这是毫无意义的练习。重写代码是在开发人员知道的内容和该知识的书面表示之间建立平衡的练习。这种一致性使开发人员能够更高效地使用代码库。重写的代码库是学习活动的副产品。 

命名是软件开发中最难的事情

如果源代码与开发人员对代码应该做什么的想法不符,许多开发人员可能会发现,对要解决的问题重建原有的思维结构和推理,还不如重写代码更容易。

请注意,我明确解决了代码库从一个开发人员转移到另一个开发人员的场景。代码所有权[6],无论是遵循个人模型还是共享模型,都是未来发布的主题。

如果没有命名练习,我无法结束本节。在完成代码重写活动并形成新的语义网络来表示他们对世界的解释之后,该开发人员为新概念选择旧名称的几率相对较低。

自我可能起到了一定的作用——命名事物就像在世界上留下一个小印记——但至少有一个非常务实的原因:一个带有旧名称的新概念必然会让熟悉旧概念的人感到困惑——见这个关于传播激活[7]主题的优秀会议供参考。

新的现实产生新的名字。


 8 
结论



在软件开发中命名(某物)是创建抽象世界这一复杂过程的顶点。

这些世界的某些部分是物理世界的直接代表,而其他部分则是全新的。

命名过程包括狭窄的任务(编写一个短的控制循环)和广泛的认知练习(创建新的抽象)。

缺乏历史参考——软件开发是一门相对较新的学科——需要经常使用不完美的隐喻来解释新范式。

前所未有的技术进步需要不断增加的抽象层,以将现代系统令人难以置信的能力分层。

其中许多挑战是我们这个行业年轻时所固有的,但我们可以从其他领域借鉴各种概念来帮助我们解释、调和和解决这些问题。 

相关链接:

  1. https://en.wikipedia.org/wiki/Learning_theory_(education)
  2. https://www.linkedin.com/pulse/role-metaphors-software-dale-stephenson/
  3. https://en.wikipedia.org/wiki/Turtles_all_the_way_down
  4. https://en.wikipedia.org/wiki/Cognitive_load
  5. https://en.wikipedia.org/wiki/Constructivism_(philosophy_of_education)
  6. https://wiki.c2.com/?CodeOwnership
  7. https://www.khanacademy.org/test-prep/mcat/processing-the-environment/cognition/v/semantic-networks-and-spreading-activation


推荐阅读:


分布式实验室策划的《Kubernetes实战集训营》正式上线了。这门课程通过通过5天线上培训,40个小时直播,15个随堂练习,50天课后辅导,把Kubernetes的60多个重要知识点讲给你,并通过实战帮你掌握Kubernetes。培训重实战、重项目、更贴近工作,边学边练,2月25日正式开课。

命名是软件开发中最难的事情

原文始发于微信公众号(分布式实验室):命名是软件开发中最难的事情

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月20日21:32:13
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   命名是软件开发中最难的事情http://cn-sec.com/archives/1560430.html

发表评论

匿名网友 填写信息