首页 文章

什么是自我记录代码,是否可以替换记录良好的代码? [关闭]

提问于
浏览
244

我有一位同事坚持认为他的代码不需要评论,而是“自我记录” .

我已经回顾了他的代码,虽然它比我见过其他代码生成的代码更清晰,但我仍然不同意自我编写代码是完整和有用的以及评论和记录的代码 .

帮助我理解 his 的观点 .

  • 什么是自我记录代码

  • 它真的可以取代评论和记录良好的代码

  • 是否存在比记录良好和注释的代码更好的情况

  • 是否存在代码无法在没有注释的情况下进行自我记录的示例

也许这只是我自己的局限,但我不知道它是如何成为一种好的做法 .

这并不是一个争论 - 请不要提出为什么评论和记录良好的代码是高优先级的原因 - 有很多资源显示这一点,但它们并不能让我的同行相信 . 我相信我需要更充分地理解他的观点来说服他 . 如果必须,请开始一个新问题,但不要在此争论 .

哇,快速反应!请阅读所有现有答案,并为答案提供评论,而不是添加新答案,除非您的答案与此处的其他答案完全不同 .

此外,那些反对自我记录代码的人 - 这主要是为了帮助我理解自我记录代码福音传道者的观点(即积极方面) . 如果你不留下话题,我希望别人会贬低你 .

30 回答

  • 13

    区别在于“什么”和“如何” .

    • 你应该记录例程"what" .

    • 除非出现特殊情况(例如,请参阅特定的算法文件),否则不应该记录"how" . 这应该是自我记录的 .

  • 13

    在我工作的公司中,其中一位程序员将以下内容粘贴在她的显示器顶部 .

    “记录你的代码就像维护它的人是一个知道你住在哪里的同性恋疯子 . ”

  • 4

    有人说过

    1)只为难以理解的代码编写注释 . 2)尽量不要编写难以理解的代码 .

  • 4

    “自我记录”代码背后的想法是代码中的实际程序逻辑非常清楚,足以向任何人解释阅读代码不仅代码正在做什么,而且为什么要这样做 .

    在我看来,真正的自我记录代码的想法是一个神话 . 代码可以告诉你正在发生的事情背后的逻辑,但它无法解释 why 它是以某种方式完成的,特别是如果有多种方法来解决问题 . 仅仅因为这个原因它永远不会取代 well commented 代码 .

  • 5

    在我看来,任何代码都应该是自我记录的 . 在良好的,自我记录的代码中,您不必解释每一行,因为每个标识符(变量,方法,类)都有一个清晰的语义名称 . 实际上有更多的评论实际上使得阅读代码更加困难(!),所以如果你的同事

    • 为每个类,成员,类型和方法AND编写文档注释(Doxygen,JavaDoc,XML注释等)

    • 清楚地注释代码的任何非自记文件的部分

    • 为解释意图的每个代码块写一个注释,或者代码在更高的抽象级别上执行的操作(即查找大于10 MB的所有文件而不是遍历目录中的所有文件,测试文件大小是否大于10 MB,如果为真,则收益率返回)

    在我看来,他的代码和文档很好 . 请注意,自我记录的代码确实 not 意味着不应该有任何注释,而只是应该没有不必要的注释 . 但是,通过阅读代码(包括注释和文档注释),可以立即了解代码的作用和原因 . 如果"self-documenting"代码比注释代码需要更长的时间来理解,那么它实际上并不是自我记录的 .

  • 5

    好吧,因为这是关于注释和代码,让我们看看一些实际的代码 . 比较这个典型代码:

    float a, b, c; a=9.81; b=5; c= .5*a*(b^2);
    

    这个自我记录的代码,显示了正在做的事情:

    const float gravitationalForce = 9.81;
    float timeInSeconds = 5;
    float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
    

    然后是这个记录的代码,它更好地解释了为什么要这样做:

    /* compute displacement with Newton's equation x = vₒt + ½at² */
    const float gravitationalForce = 9.81;
    float timeInSeconds = 5;
    float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
    

    最终版本的代码作为文档,需要零注释:

    float computeDisplacement(float timeInSeconds) {
        const float gravitationalForce = 9.81;
        float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
        return displacement;
    }
    

    这是一个糟糕的评论风格的例子:

    const float a = 9.81; //gravitational force
    float b = 5; //time in seconds
    float c = (1/2)*a*(b^2) //multiply the time and gravity together to get displacement.
    

    在最后一个示例中,当变量应该被描述性地命名时使用注释,并且当我们可以清楚地看到操作是什么时,总结操作的结果 . 我希望任何一天都能有自己记录的第二个例子,也许这就是你的朋友在谈到自我记录的代码时所说的话 .

    我会说这取决于你在做什么的背景 . 对我来说,在这种情况下,自我记录的代码可能就足够了,但是详细说明背后的方法背后的方法(在这个例子中,方程式)也很有用 .

  • 167

    代码本身始终是对代码所做内容的最新解释,但在我看来,它很难解释意图,这是评论最重要的方面 . 如果它写得正确,我们已经知道代码的作用,我们只需要知道它为什么会这样做!

  • 3

    我认为问一个特定的代码行是否是自我记录是相关的,但最后如果你不理解一段代码的结构和功能,那么大多数时候评论都没有帮助 . 以amdfan的“正确评论”代码为例:

    /* compute displacement with Newton's equation x = v0t + ½at^2 */
    const float gravitationalForce = 9.81;
    float timeInSeconds = 5;
    float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
    

    这段代码很好,但以下内容在大多数现代软件系统中都具有相同的信息,并明确承认使用牛顿计算是一种选择,如果其他一些物理范例更合适,可以改变它:

    const float accelerationDueToGravity = 9.81;
    float timeInSeconds = 5;
    float displacement = NewtonianPhysics.CalculateDisplacement(accelerationDueToGravity, timeInSeconds);
    

    根据我个人的经验,很少有“正常”的编码情况,你绝对需要评论 . 例如,您最终经常推出自己的算法?基本上所有其他事情都是构建系统的问题,以便编码人员能够理解使用中的结构以及驱使系统使用这些特定结构的选择 .

  • 15

    我忘记了从哪里得到这个,但是:

    程序中的每条评论都像对读者道歉 . “我很抱歉,我的代码非常透明,你无法通过查看来理解它” . 我们必须接受我们并不完美但努力做到完美,并在我们需要的时候道歉 .

  • 36

    自我记录代码是“干”(不要重复自己)的一个很好的例子 . 不要在代码本身的注释中复制信息 .

    而不是解释变量的用途,重命名变量 .

    而不是解释代码的简短片段,将其提取到方法中并为其指定一个描述性名称(可能是评论文本的缩短版本) .

    而不是解释复杂测试的作用,将其提取到方法中并给它一个好名字 .

    等等 .

    在此之后,您最终得到的代码不需要那么多解释,它解释了自己,因此您应该删除仅在代码中重复信息的注释 .

    这并不意味着您根本没有任何评论,有些信息无法放入代码中,例如有关意图的信息(“为什么”) . 在理想情况下,代码和注释相互补充,每个都添加唯一的解释值而不会在另一个中复制信息 .

  • 3

    自我记录代码是一种很好的做法,如果正确完成,可以轻松传达代码的含义,而无需阅读太多注释 . 特别是在团队中的每个人都很好理解域名的情况下 .

    话虽如此,评论对新来者或测试人员或生成文档/帮助文件非常有用 .

    自我记录代码必要的评论将大大有助于团队中的人们 .

  • 167

    首先,很高兴听到您的同事的代码实际上比您看到的其他代码更清晰 . 这意味着他可能不会使用“自我记录”作为懒得评论他的代码的借口 .

    自我记录代码是一种代码,它不需要自由文本注释,以便知情读者理解它在做什么 . 例如,这段代码是自我记录的:

    print "Hello, World!"
    

    这是这样的:

    factorial n = product [1..n]
    

    这是这样的:

    from BeautifulSoup import BeautifulSoup, Tag
    
    def replace_a_href_with_span(soup):
        links = soup.findAll("a")
        for link in links:
            tag = Tag(soup, "span", [("class", "looksLikeLink")])
            tag.contents = link.contents
            link.replaceWith(tag)
    

    现在,这个“知情读者”的想法是非常主观和情境化的 . 如果您或其他任何人在跟踪您的同事代码时遇到问题,那么他应该重新评估他对知情读者的看法 . 必须假定对所使用的语言和库有一定程度的熟悉程度,以便调用代码自我记录 .

    我在编写"self-documenting code"时看到的最好的论点是,它避免了自由文本评论的问题,因为它不符合编写的代码 . 最好的批评是,虽然代码可以自己描述它在做什么以及如何做,但它无法解释为什么某些事情以某种方式完成 .

  • 3

    为了:

    • 自我记录代码是向读者明确表达其意图的代码 .

    • 不完全 . 评论总是有助于评论选择特定策略的原因 . 但是,解释代码部分正在做什么的注释指示代码不够自我记录并可能使用一些重构..

    • 评论谎言并且过时了 . 代码总是告诉更有可能说实话 .

    • 我从来没有见过一个案例,如果没有评论,代码的内容就不能充分清晰;然而,就像我之前所说的那样,有时必须/有帮助包括对原因的评论 .

    然而,重要的是要注意,真正的自我记录代码需要大量的自我和团队纪律 . 你必须学会更具声明性地编程,并且你必须非常谦虚并且避免使用“聪明”的代码来支持代码,这些代码很明显,似乎任何人都可以编写代码 .

  • 5

    首先,请考虑以下代码段:

    /**
     * Sets the value of foobar.
     *
     * @foobar is the new vaue of foobar.
     */
     public void setFoobar(Object foobar) {
         this.foobar = foobar;
     }
    

    在此示例中,每3行代码有5行注释 . 更糟糕的是 - 评论不会通过阅读代码添加任何您看不到的内容 . 如果你有10个像这样的方法,你可以得到'评论失明',而不会注意到一个偏离模式的方法 .

    如果当然,更好的版本应该是:

    /**
     * The serialization of the foobar object is used to synchronize the qux task.
     * The default value is unique instance, override if needed.
     */
     public void setFoobar(Object foobar) {
         this.foobar = foobar;
     }
    

    不过,对于琐碎的代码,我更喜欢没有评论 . 在代码之外的单独文档中更好地解释了意图和整体组织 .

  • 6

    当您阅读“自我记录代码”时,您会看到它正在做什么,但您无法总是猜测它为什么以这种特定方式进行 .

    有许多非编程约束,如业务逻辑,安全性,用户需求等 .

    当您进行维护时,那些backgorund信息变得非常重要 .

    只是我的一小撮盐......

  • 366

    您可能希望向您的同事指出的一件事是,无论他的代码如何自我记录,如果考虑并放弃其他替代方法,除非他用该信息评论代码,否则信息将丢失 . 有时同样重要的是要知道替代方案被考虑以及为什么决定反对,代码注释最有可能随着时间的推移而存在 .

  • 9

    您是否听说过Donald Knuth的"WEB"项目实施他的Literate Programming概念?它's more than self-documenting code; it'更像是可以作为代码编译和执行的文档 . 我不知道今天使用了多少 .

  • 92

    代码是自我记录的观点让我发疯 . 特定的代码行或子算法可能确实是自我文档化的,但它在更大的图片中的目的根本就不是 .

    一两个月前我对此感到非常沮丧,我写了一篇完整的博客文章,描述了我的观点 . Post here .

  • 4

    自记录代码通常使用与代码正在完全匹配的变量名称,以便易于理解正在发生的事情

    但是,这种“自我记录代码”永远不会取代评论 . 有时代码太复杂,自编文档代码不够,特别是在可维护性方面 .

    我曾经有一位坚信这个理论的教授事实上,我记得他最好的事情就是"Comments are for sissies"
    起初我们所有人都感到惊讶,但这是有意义的 .
    然而,情况是,即使您可能能够理解代码中发生了什么,但是经验不足的人可能会落后于您并且不了解正在发生的事情 . 这是评论变得重要的时候 . 我知道很多次我们不相信它们很重要但很少有人不需要评论 .

  • 14

    我很惊讶没有人带来“Literate Programming ", a technique developed in 1981 by Donald E. Knuth of TeX and "计算机程序设计艺术”的名气 .

    前提很简单:由于代码必须由人理解,并且编译器简单地抛弃了注释,为什么不给每个人他们需要的东西 - 代码意图的完整文本描述,不受编程语言要求的限制,对于人类读者和编译器的纯代码 .

    Literate Programming工具通过为您提供的文档提供特殊标记来实现此目的工具应该是什么部分来源,什么是文本 . 程序稍后将源代码部分从文档中删除并组装代码文件 .

    我在网上找到了一个例子:http://moonflare.com/code/select/select.nw或HTML版本http://moonflare.com/code/select/select.html

    如果您可以在图书馆中找到Knuth的书(Donald E. Knuth,Literate Programming,Stanford,California:语言和信息研究中心,1992,CSLI讲义,第27号),您应该阅读它 .

    这是自我记录的代码,完整的推理和所有 . 甚至做了一个很好的文档,其他一切都是写得很好的评论:-)

  • 3

    我的观点写在这篇文章中:

    The one single tip to document your code.

    摘抄:

    为什么不重写你的逻辑以使它们不言而喻,而不是写很多评论来解释你的程序的微妙行为?而不是记录方法正在做什么,为什么不为该方法选择一个明确的名称?而不是标记您的代码以指示未完成的工作,为什么不抛出NotImplementedException()?而不是担心你的评论对你的老板,你的同事或任何阅读代码的人来说是否足够礼貌,为什么不停止担心不写作呢?您的代码越清晰,就越容易维护它,扩展它,在未来的版本上进行处理 . 代码越少,就越不需要对其进行评论 . 评论越多,维护成本越高 .

  • 5

    我想为许多有效的答案提供一个更多的视角:

    什么是源代码?什么是编程语言?

    这些机器不需要源代码 . 他们很高兴跑步装配 . 编程语言对我们有利 . 我们不想写汇编 . 我们需要了解我们正在撰写的内容 . 编程是关于编写代码 .

    你能读懂你写的吗?

    源代码不是用人类语言编写的 . 它已经尝试过(例如FORTRAN),但并不完全成功 .

    源代码不能有歧义 . 这就是为什么我们必须在其中加入比文本更多的结构 . 文本仅适用于上下文,我们在使用文本时认为这是理所当然的 . 源代码中的上下文始终是explisit . 在C#中思考“使用” .

    大多数编程语言都有冗余,因此当我们不连贯时,编译器可以捕获我们 . 其他语言使用更多推理并尝试消除冗余 .

    计算机不需要类型名称,方法名称和变量名称 . 它们被我们用于参考 . 编译器不理解语义,这是我们使用的 .

    编程语言是人与机器之间的语言桥梁 . 它必须是我们可写的并且对它们可读 . 次要要求是它应该对我们可读 . 如果我们擅长允许并且擅长构造代码的语义,那么即使对我们来说,源代码也应该易于阅读 . 最好的代码不需要评论 .

    但是复杂性潜藏在每个项目中,你总是要决定把复杂性放在哪里,以及哪些骆驼可以吞下去 . 这些是使用评论的地方 .

  • 2

    自我记录代码是一个容易选择的问题,随着时间的推移代码,评论和文档分歧 . 编写清晰的代码是一个规范因素(如果你对自己严格要求) .

    对我来说,这些是我试图遵循的规则:

    • 代码应尽可能简单易读 .

    • 注释应该说明我采取的设计决策的原因,例如:为什么我使用这个算法,或者代码有限制,例如:当...时不起作用(这应该在代码中的 Contract /断言中处理)(通常在功能/程序内) .

    • 文档应列出用法(调用converntions),副作用,可能的返回值 . 它可以使用jDoc或xmlDoc等工具从代码中提取 . 因此它通常在函数/过程之外,但接近它描述的代码 .

    这意味着所有三种记录代码的方式都是紧密相连的,因此当代码更改时更有可能被更改,但不会在它们表达的内容中重叠 .

  • 2

    所谓的自我记录代码的真正问题在于它传达了它实际所做的事情 . 虽然一些评论可能有助于某人更好地理解代码(例如,算法步骤等),但它在某种程度上是多余的,我怀疑你会说服你的同行 .

    然而,在文档中真正重要的是代码中没有直接显示的东西:潜在的意图,假设,影响,限制等

    能够确定代码快速浏览一下代码比确定代码不能做Y更容易 . 他必须记录Y ...

    您可以向他展示一个看起来很好,很明显的代码示例,但实际上并没有覆盖输入的所有基础,例如,看看他是否找到了它 .

  • 18

    我认为自我记录代码是评论的一个很好的替代品 . 如果您需要注释来解释代码的方式或原因,那么您有一个函数或变量名称应该被修改为更具说明性 . 关于他是否会通过评论弥补不足或重命名一些变量和函数以及重构代码,可以归结为编码人员 .

    但它并不能真正取代你的文档,因为文档是你给别人解释如何使用你的系统而不是它如何做的事情 .

    编辑:我(可能还有其他所有人)可能应该有一个数字信号处理(DSP)应用程序应该得到很好的评论 . 这主要是因为DSP应用程序本质上是2个用于数组值的循环和添加/乘法/等等所述值...改变程序你改变其中一个数组中的值...需要几个评论来说明什么你在这种情况下做;)

  • 4

    在编写数学代码时,我有时发现编写长篇,类似于论文的注释,解释数学,代码使用的符号约定以及它们如何组合在一起是有用的 . 我们在这里谈论数百行文档 .

    我尽量让我的代码尽可能自我记录,但是当我几个月后回来工作时,我确实需要阅读解释,以避免使用它 .

    现在,当然,对于大多数情况来说,这种极端措施并不是必需的 . 我认为故事的寓意是:不同的代码需要不同数量的文档 . 有些代码可以写得很清楚,不需要注释 - 所以写得清楚,不要在那里使用注释!

    但是很多代码确实需要注释才有意义,所以尽可能清楚地写出来,然后根据需要使用尽可能多的注释...

  • 2

    我认为 - 正如你们许多人所做的那样 - 要真正实现自我记录,代码需要表现出某种形式的意图 . 但我很惊讶没人提到BDD - Behavior Driven Development . 部分想法是你有自动化测试(代码)来解释你的代码的意图,否则很难显而易见 .

    Good domain modeling 
    + good names (variabes, methods, classes) 
    + code examples (unit tests from use cases) 
    = self documenting software
    
  • 3

    除了代码之外额外注释可能更清楚的几个原因:

    • 您正在查看的代码是自动生成的,因此下次编译项目时对代码的任何编辑都可能会被破坏

    • 为了获得性能增益而进行了不太直接的实现(展开循环,为昂贵的计算创建查找表等)

  • 6

    它将成为团队在其文档中所重视的全部内容 . 我建议记录为什么/意图而不是如何重要,这并不总是在自我记录代码中捕获 . 获取/设置没有这些是显而易见的 - 但计算,检索等应该表达的原因 .

    如果您来自不同国家,请注意您团队的不同之处 . 用词的差异可能会影响方法的命名:

    BisectionSearch

    二分查找

    BinaryChop

    这三种方法由在3个不同大陆训练的开发人员做出同样的贡献 . 只有通过阅读描述算法的注释,我们才能识别出库中的重复 .

  • 4

    对我来说,阅读需要评论的代码就像用我不知道的语言阅读文本 . 我看到声明,我不明白它的作用或原因 - 我必须看看评论 . 我读了一个短语,我需要查看字典才能理解它的含义 .

    编写代码可以很容易地自我记录它的作用 . 告诉你为什么它这样做更适合评论,但即使这里代码可以更好 . 如果你在每个抽象层次上理解你的系统,你应该尝试组织你的代码

    public Result whatYouWantToDo(){
      howYouDoItStep1();
      howYouDoItStep2();
      return resultOfWhatYouHavDone;
    }
    

    方法名称反映了您的意图,方法主体解释了您如何实现目标 . 无论如何,你无法在其 Headers 中讲述整本书,因此仍需要记录系统的主要抽象,以及复杂的算法,非平凡的方法 Contract 和工件 .

    如果你的同事产品真正自我记录的代码 - 幸运的是你和他 . 如果您认为您的同事代码需要评论 - 它需要 . 只需打开其中最重要的地方,阅读一次,看看你是否理解了一切 . 如果代码是自我记录的 - 那么你应该 . 如果不是 - 在他给你一个答案之后,问你的同事一个关于它的问题,请问为什么这个答案没有预先记录在评论或代码中 . 他可以声称代码是像他这样聪明人的自我文档,但无论如何他必须尊重其他团队成员 - 如果你的任务需要理解他的代码而他的代码没有向你解释你需要理解的一切 - 它需要评论 .

相关问题