#成熟度

ginobefun
18小时前
源自 Hacker News 上的这篇文章 🔽 文润转译: --------- 软件工程中的“品味”究竟意味着什么? 技术品味(Technical taste)和技术能力(Technical skill)是两码事。你可能技术很强但品味不足,也可能技术平平却品味独到。如同生活中的品味,技术品味有时会超前于你的实际能力:就像你即便不会下厨,也能分辨佳肴与糟粕;同样地,即便你还不具备构建某种软件的能力,也可能已经知道自己喜欢什么样的软件。技术能力可以通过学习和重复练习来提升,但好品味的养成过程则更为难以捉摸。 以下是一些衡量软件品味的指标: - 什么样的代码在你看来是“好看”的?什么样的又是“难看”的? - 哪些设计决策让你深感满意,而哪些只是“差强人意”? - 哪些软件问题会让你寝食难安,甚至下班后还在琢磨?而哪些问题你又能一笑置之? 我认为,品味是一种能够根据当前项目,选择并采纳最契合的工程价值观的能力。 为什么品味不同于能力 看到上面的指标,你可能会问:这难道不就是技术能力的一部分吗?比如说,代码看起来“好看”,不正是因为它本身就是“好代码”吗?我不这么认为。 举个例子。我个人觉得,使用 map 和 filter 的代码要比传统的 for 循环更简洁。我们很容易会认为,这代表了我在工程观点上是绝对正确的。比如,map 和 filter 通常涉及纯函数 (pure functions),更易于理解和推理,还能避免一类常见的下标错误(off-by-one iterator bugs)。这让我觉得,这并非品味之争,而是我正确、其他工程师错误的问题。 但现实当然要复杂得多。像 Golang 这样的语言,出于其设计原则,完全没有内置 map 和 filter。从性能角度看,for 循环的逻辑更容易分析,也更容易扩展到其他迭代策略(比如一次处理两个元素)。我个人对后者的关注度,不如对前者的关注度高——这就是为什么我不常用 for 循环——但如果因此就说偏爱 for 循环的工程师技术能力不行,那就太傲慢了。很多时候,他们拥有的技术能力恰恰是我所不具备的。他们只是在乎的东西不一样。 换言之,我们的分歧源于价值观的差异。关于这一点,我曾在《我不知道如何构建软件,你也不知道》一文中探讨过。即使那些重大的技术争论确实存在标准答案,也没有哪个在职的软件工程师敢说自己样样精通,因为一个人的职业生涯所能积累的经验终究有限。我们都在一定程度上依赖于个人经验,依赖于自己那套特定的工程价值观。 工程品味的本质是什么 软件工程中的几乎每一个决策都是一种权衡 (tradeoff)。你很少会遇到一个选项完全优于另一个选项的情况。更多时候,每个选项都有其利弊。我们常常需要在不同的工程价值观之间做出艰难的取舍:例如,当性能优化到一定程度后,你可能就很难在不牺牲代码可读性的前提下继续提升性能了。 在我看来,能否真正理解这一点,是衡量软件工程师成熟度的最大标志。不成熟的工程师在做决策时往往很固执,他们认为做 X 或者 Y 总是更好的。而成熟的工程师则乐于权衡一个决策的利弊,因为他们知道每种选择都有其优缺点。关键不在于判断技术 X 是否优于 Y,而在于在当前这个特定场景下,X 的好处是否大于 Y。 换句话说,不成熟的工程师对自己的品味过于执着。他们知道自己喜欢什么,却误将这种喜好当作了放之四海而皆准的工程原则。那么,一个工程师的品味究竟是由什么构成的呢? 我认为,你的工程品味,由你认为最重要的那套工程价值观所构成。例如: - 可靠性 (Resiliency) 如果某个基础设施组件(如服务、网络连接)发生故障,系统是否仍能正常运行?能否在无人干预的情况下恢复? - 速度 (Speed) 软件的运行速度与理论极限相比如何?在关键路径 (hot path) 上是否存在不必要的计算? - 可读性 (Readability) 代码是否一目了然,便于新工程师上手?函数是否相对简短且命名得当?系统文档是否完善? - 正确性 (Correctness) 系统是否可能出现无效状态?系统是否通过测试、类型系统和断言进行了严格的约束?测试是否使用了模糊测试 (fuzzing) 等技术?在极端情况下,程序是否通过 Alloy 等形式化方法被证明是正确的? - 灵活性 (Flexibility) 系统能否毫不费力地扩展?进行一次变更有多容易?如果我需要修改某个功能,需要同时改动多少个不同的地方? - 可移植性 (Portability) 系统是否被绑定在特定的运行环境上(比如 Windows 或 AWS)?如果需要将系统部署到别处,能否在不投入大量工程工作量的情况下完成? - 可扩展性 (Scalability) 如果流量增长 10 倍,系统会崩溃吗?100 倍呢?系统是需要过度配置资源,还是可以自动扩展?哪些瓶颈需要工程师介入解决? - 开发速度 (Development speed) 如果我需要扩展系统,需要多长时间?是大多数工程师都能着手处理,还是需要领域专家的介入? 当然,工程价值观还有很多,比如:优雅性、技术的现代性、开源技术的使用、系统运行的经济成本等等。所有这些都很重要,但没有哪个工程师会对所有这些方面给予同等的关注。 你的品味,正取决于你将哪些价值观置于最高优先级。例如,如果你看重速度和正确性胜过开发速度,你可能会更喜欢 Rust 而不是 Python。如果你看重可扩展性胜过可移植性,你可能会主张深度利用你所在平台(如 AWS)的特性和工具。如果你看重韧性胜过速度,你可能会希望将流量分散到不同区域。 我们还可以将这些价值观进行更细致的拆分。两个都非常关心可读性的工程师,可能会因为一个推崇短函数,另一个推崇短调用栈而产生分歧。两个都关心正确性的工程师,也可能因为一个看重详尽的测试套件,另一个信奉形式化方法而意见不合。但原理是相通的——有太多工程价值观值得我们去追求,而它们之间又常常相互冲突,这就迫使每位工程师必须有所取舍。 如何识别“坏品味” 虽然我前面说所有的价值观都很重要,但这并不意味着不存在“坏品味”。在软件工程的语境下,坏品味意味着你所偏爱的价值观与你正在做的项目格格不入。 我们大多数人都和这样的工程师共事过。他们加入你的项目后,就开始鼓吹某些东西——形式化方法、用 Golang 重写、Ruby 元编程、跨区域部署等等——因为这些东西在他们过去的项目中效果很好。无论这是否适合你当前的项目,他们都会极力推崇,因为这是他们喜欢的方式。不知不觉中,你发现自己为了让一个内部指标看板达到 99.999% 的可靠性,牺牲了代码的可理解性,导致没有一个初级工程师能看懂。 换句话说,大多数坏品味都源于僵化和缺乏变通。我总是对那些以“这是最佳实践”来为自己决策辩护的工程师持保留态度。没有任何工程决策在所有情境下都是“最佳实践”!你必须针对你面临的具体问题,做出正确的决策。 这带来一个有趣的结果:品味差的工程师就像一块坏了的罗盘。如果你恰好站在对的位置,坏罗盘依然能指向北方。只有当你开始移动,它才会把你引向歧途。同样,许多品味差的工程师在特定的领域内可能非常高效,因为在那个领域里,他们的个人偏好正好与项目需求相符。但当他们被调到不同的项目或岗位,或者当项目本身的性质发生变化时,问题就立刻暴露出来了。要知道,工作内容很少会一成不变,尤其是在 2021年后这些充满挑战的时代 里。 如何识别“好品味” 相比技术能力,好品味要难以捉摸得多。因为与技术能力不同,好品味是针对你面临的特定技术问题,选择正确工程价值观组合的能力。因此,判断一个人是否有好品味要困难得多:你无法通过玩具问题 (toy problems) 或技术问答来检验它。你需要一个真实的复杂问题,以及它所附带的各种错综复杂的现实世界背景。 如何判断自己是否有好品味?如果你深度参与设计的项目都取得了成功,那么你可能就拥有好品味。如果你只是执行任务(比如完成一个个工单),那么可以这样判断:那些你认同其设计决策的项目取得了成功,而那些你不认同的项目则步履维艰。重要的是,你需要经历不同类型的项目。如果只有一个项目,或者反复做同一种项目,那可能只是说明你恰好适合那个领域。即使你经历过许多不同类型的项目,也不能保证你在不熟悉的领域里同样拥有好品味。 那么,如何培养好品味呢?这很难说,但我建议多接触不同类型的项目,密切关注哪些项目(或项目的哪些部分)进展顺利,哪些部分举步维艰。你应该专注于保持灵活性:尽量不要对“什么是写软件的正确方式”形成固执的、普适性的看法。我自己的好品味是慢慢积累起来的。当然,我也不认为你不能快速获得它。我相信编程领域也存在一些天赋异禀、品味超乎其经验的奇才,就像其他领域一样。