编写可维护的代码是一种沟通技巧 - Max Chernyak


编写可维护的代码很容易。只需保持方法和参数列表简短,名称和注释较长,并遵循样式指南。
正如一位著名记者曾经写道:“对于每一个复杂的问题,都有一个清晰、简单和错误的答案。”
使代码难以维护的不是样式和形状。这是在缺乏明确的如何代码工作,它代表什么以及为什么它以这种方式被写?
我将这些问题称为 “怎么样?”, “什么?”,以及 “为什么?” 简称。
问题很简单,但回答它们却没有什么直截了当的。
可维护的代码是能够雄辩而周到地向读者传达它是如何实现的、什么实现的以及为什么实现的代码。
 

怎么样?
指编写程序或算法的表达程度。
您必须将复杂的算法分解为清晰的步骤。你必须寻找好的比喻来帮助人们理解你的抽象代码。换句话说,您必须编写不断指导其他工程师的代码。这种清晰的沟通水平是罕见的,但伟大的代码库也是如此。
冗长的、令人费解的,或者又是完全的胡言乱语:
def r(s1, s2, s3)
  [s3.bytes, [32], s1.bytes, [10]*2, s2.bytes].map { |ba|
    ba.flat_map(&:chr).inject { |v, a| "#{a}#{v}" }.reverse
  }.inject(&:+)
end

使代码易于理解。您可以看到字符串正在连接:
def r(s1, s2, s3)
  s3 + " " + s1 + "\n\n" + s2
end

 
什么?
“将复杂性隔离在一个永远不会被发现的地方几乎与完全消除复杂性一样好。”
只要你的函数被很好地隔离、经过很好的测试和很好的命名,你就很难写出糟糕的代码。
在下面的示例中,名称太长并且考虑到它们的上下文是多余的。
def render_email_with_a_greeting(email_recipient_name_string_for_rendering_email, email_body_string_for_rendering_email, email_greeting_string_for_rendering_email)
  email_greeting_string_for_rendering_email + " " + email_recipient_name_string_for_rendering_email + "\n\n" + email_body_string_for_rendernig_email
end

好的代码:
def render_email(recipient_name, body, greeting: 'Hello,')
  greeting + " " + recipient_name + "\n\n" + body
end

 
为什么?
“给光,人们就会找到路。”
一些思想流派认为所有代码注释都是代码表达的失败。也就是说,代码是最可靠的事实来源:
重要的是要学会:

  1. 查明哪些决策实际上需要上下文。通常,需要解释的决定来自以下四种情况之一:1) 您的决定存在不明显的商业原因 2) 您进行了大量研究以做出决定 3) 您对您选择的解决方案或 4) 您在代码审查中被问到了一个问题。在每种情况下,留下澄清评论可能是个好主意。
  2. 确定您的受众所需的详细程度。阅读你的代码注释的人很可能是熟悉你公司内部术语和流程的有经验的程序员。依靠您共享的知识进行有效沟通。

不好代码:
# Keyword argument `greeting` has a default value.
def render_email_to_send(recipient_name, body, greeting: 'Hello,')
  # Emails can be plain and html, and while most email
  # clients support html, it's a good practice to add plain
  # text versions as a fallback.
  greeting + " " + recipient_name + "\n\n" + body
end

好代码:
def render_email(recipient_name, body, greeting: 'Hello,')
  greeting + " " + recipient_name + "\n\n" + body
end

在查看上面的代码时,我们可以假设熟悉基础知识并确定几个潜在的问题:
  • 为什么我们需要支持自定义问候语?
  • 既然我们使用\n,那么这个函数是否只用于纯文本电子邮件?
  • (对于 ruby​​ists 那里)为什么我们连接+而不是"#{interpolation}"?

这是解决它们的一种方法。
# We allow custom greetings because marketing wants to be able to
# personalize them by time of day, e.g. "Good Afternoon, Person".
def render_plain_text_email(recipient_name, body, greeting: 'Hello,')
  # We avoid interpolation because we want nil values to error out.
  # Helps prevent missing content in sent emails.
  greeting +
" " + recipient_name + "\n\n" + body
end

现在有代码注释解释了为什么我们允许自定义问候语并避免插值。

或者,我们可以考虑通过重命名greeting为personalized_greeting来消除评论:
def render_plain_text_email(recipient_name, body, personalized_greeting: 'Hello,')
  # We avoid interpolation because we want nil values to error out.
  # Helps prevent missing content in sent emails.
  personalized_greeting + " " + recipient_name + "\n\n" + body
end

 
有用的框架
“如果我有一个小时来解决一个问题,而我的生命取决于解决方案,那么我会花前 55 分钟来确定要问的正确问题,一旦我知道正确的问题,我就可以在不到五分钟的时间内解决问题。 ”
当我们与工程师同事和利益相关者合作时,我们会进行三种最困难的交流:1) 提供反馈(在代码审查中)2)协商(在估计中)和 3)传达抽象概念(在代码中)。这些对话可能会引起焦虑,我们每天都会进行多次!这 “怎么样?”, “什么?” 和 “为什么?” 框架可以帮助我们组织我们的思想。

  1. 在进行代码审查时,您可以更具体地指出问题:
    • “我明白你在做什么,但很难理解它是如何运作的。”
    • “我看到这是如何工作的,以及为什么我们需要这个,但提取的方法将使它更容易理解什么这一块在做什么。”
    • “我看到正在完成的工作,以及它是如何完成的,但我不清楚我们为什么做出这个特别的选择。”
  • 在协商重构最后期限时,您现在拥有的语言可以帮助利益相关者准确理解您要实现的目标:
    • “很难理解这段代码在幕后是如何工作的。在我们自信地改变它之前,我们需要进行重构。”
    • “这个代码需要被打破了,所以我们可以更容易地遵循什么就做什么。”
    • “我们许多的原因,为什么从来没有写下来,所以我们想尝试,并添加一些上下文的代码库,我们忘记了。”
  • 最后,它提供了一个清单,用于在您与团队共享之前反思您自己的工作:
    • “读者会很容易理解我的代码是如何工作的吗?”
    • “我的名字是否清楚地表达了我的代码完成的任务?”
    • “我是否给出了适当的上下文来表达我为什么这样编写代码?”