2018年,我在一个大型项目开发到中期时加入了该项目。原来的工程师已经离职,留下了复杂且缺乏文档的代码。使用这类代码很有挑战性,因为你无法区分基础设施代码和业务领域代码。这使得调试变得困难,改动也变得不可预测,因为你不知道会产生什么影响。这就像试图编辑一本你不理解其中词汇的书。
许多工程师认为成功的标志是代码能够编译。我认为成功的标志是另一个工程师(或者六个月后的你)能够理解你代码的”为什么”。原来的工程师通过不记录文档和使用晦涩的命名,给后来的工程师设置了障碍。这些命名有时是了解前一位工程师思维过程的唯一窗口。
Donald Knuth曾经说过:
程序是为人类阅读而写的,只是附带地供计算机执行。— Donald Knuth
命名
命名很困难,因为它需要标记和定义一个代码片段在应用程序中的位置和方式。
Phil Karlton在Netscape工作时曾观察到:
计算机科学中只有两件难事:缓存失效和命名。
— Phil Karlton
我们通过使用的词汇和名称来看待我们的代码。名称为下一位工程师创造了一种理解的语言。这种语言描绘了作者如何在业务领域和编程语言之间架起桥梁的图景。
20世纪上半叶的哲学家Ludwig Wittgenstein曾说过:
我的语言的极限意味着我的世界的极限。— Ludwig Wittgenstein
我们软件的语言只有在我们使用的名称一样具有描述性时才能具有描述性。使用模糊的名称会模糊软件的目的;使用描述性的名称会带来清晰和理解。
想象一下访问一个你不会说当地语言的国家。一个简单的请求,比如询问洗手间的位置,会引来困惑的目光。无法沟通是令人沮丧的,甚至可能令人害怕。当工程师面对令人困惑、不清楚,甚至更糟的是误导性的名称时,会有同样的感受。
这种感受最好通过亲身体验来理解。
体验
检查第一段代码,这段代码做什么?为什么这样做?
花点时间。
public class StringHelper
{
public string Get(string input1, string input2)
{
var result = string.Emtpy;
if(!string.IsNullOrEmtpy(input1) && !string.IsNullOrEmtpy(input2))
{
result = $"{input1} {input2}";
}
return result;
}
}
上面的代码是两个字符串的简单连接。代码没有告诉你的是”为什么”。“为什么”非常重要,没有它,很难在不了解影响的情况下改变行为。当然,调查代码的使用方式可能会揭示它的”为什么”,但这正是问题所在。你不应该去发现代码的目的,而是作者应该留下线索,这是他们的责任。
让我们重新审视这段代码,但加入了一些”为什么”。
再花点时间,观察阅读这段代码时你感受到的差异。
public class FirstAndLastNameFormatter
{
public string Concatenate(string firstName, string lastName)
{
var fullName = string.Emtpy;
if(!string.IsNullOrEmtpy(firstName) && !string.IsNullOrEmtpy(lastName))
{
fullName = $"{firstName} {lastName}";
}
return fullName;
}
}
“为什么”让代码活了起来,有一个故事可以阅读。
沟通
将意图和设计传达给下一位工程师,使软件能够生存和发展,因为如果工程师无法修改软件,它就会死亡。这是一个悲剧,尤其是当它是由于设计不良和缺乏表达性而导致的时候——这两者都是可以通过知识来预防的。
为下一位工程师做个好事,在你的代码中保持表达性。使用描述性的名称并捕捉”为什么”,因为谁知道呢,下一位工程师可能就是你。
作者:Chuck Conway 是一位 AI 工程师,拥有近 30 年的软件工程经验。他构建实用的 AI 系统——内容管道、基础设施代理和解决实际问题的工具——并分享他沿途的学习成果。在社交媒体上与他联系:X (@chuckconway) 或访问他的 YouTube 和 SubStack。