如何记录我的代码以进行最短时间检查?[关闭]


22

我想记录我的代码,以使几个月后再次阅读和浏览代码的需求降至最低。

我知道有不同类型的文档(在源代码中和外部,序列图等中)。

我只想知道什么是记录我的代码的有效方法,所以几个月后我想看我的代码时,我花更少的时间在阅读代码和理解代码流上。


42
以后花更少时间阅读代码的最佳方法是编写更清晰更易理解的代码。
梅尔


文档取决于目标。如果您要面向开发人员,那么代码中的注释将非常有用。如果您要与分析师联系,则概述图也很有用。如果您是针对精通技术的受众,请制作用户指南。
Laiv

@Laiv好吧,鉴于我自己的代码的开发人员,也许还有其他开发人员的代码。
Hamed_gibago18年

最大的事情是使代码保持较小。如果在问题跟踪系统中实现某个项目所需的代码很大,那么您的团队可能需要学习如何对其进行分解,以使所审查的代码数量不会太多。
Berin Loritsch '18

Answers:


16

我必须承认,我不同意其他答案所建议的某些内容,因此我将丢掉两美分。

评论

文档对于陌生人阅读您的代码非常有帮助。通常,许多事情不够详尽,无法立即阅读和理解,因此您应该解释自己在做什么。

编辑:注释部分的讨论指出了一些正确的方法–编写错误代码时通常会进行过度注释。

对您的工作发表评论应该准确无误,但我认为绝对应该存在。每15行代码至少要有一条注释。例如,在代码块的上方,添加一行关于您正在执行的操作:

def login(username: str, password: str, create_session: bool = True):

    # Filter the user we need from the database
    hash = md5(password)
    users = db.table("users", db_entities.USER)
    results = [x for x in users.query(lambda c: c.get("username") == username and c.get("password_hash") == hash)]


    if len(results) == 0:
        return None, None
    else:
        # Create a login session record in the database.
        if create_session:
            sessions = db.table("sessions", db_entities.SESSION)
            ses = sessions.new()
            ses.set("username", username) \
                .set("expiery", 31536000 + time.time())
            sessions.update(ses)
            return results[0], ses
        else:
            return results[0], None

解释最小的评论为什么什么你做是整个代码非常有帮助。我不同意这个答案

如果遇到包含注释的代码,我会做最坏的准备:代码可能很糟糕,老实说注释也很糟糕。

很多时候,优雅地记录了良好的代码。确实,糟糕的程序员会看到他们的文档,例如“好吧,我的代码很糟糕,让我们添加一些句子使其更清楚”。

是的,尽管这种情况经常发生,但编写干净代码的优秀程序员也确实希望确保他们返回自己的代码,并理解为什么他们希望自己的函数如此运行,或者为什么需要这样做似乎有点多余的行,等等。

是的,解释明显的事情的注释,不清楚的注释,只是为了确保“已记录此代码,是的,无论如何”而组合在一起的注释都是代码味道。它们使阅读代码更加困难和烦人。(在下面添加示例)

# Logging into Gmail when the module is imported
_client = login()
def get_client():
    global _client
    return _client

澄清示例:“不,Sherlock。是否_client = login()登录到邮件服务?OMG!”

更明确:该login()方法login()与上面示例中的方法无关。

但是,这样做的评论符合标准,解释为什么的,而不是如何的,并回答正确的问题,是非常非常(非常)有帮助。

内联评论

一件事你应该不是(如果我能写大,我会)做的,就是在同一行的代码编写您的意见。它使注释非常特定于行,这完全错过了注释代码的目的。

例如,错误的内联注释:

outer = MIMEText(details["message"]) # Constructing a new MIMEText object
outer["To"] = details["to"] # Setting message recipient
outer["From"] = "xAI No-Reply" # Setting message sender
outer["Subject"] = details["subject"] # Setting message subject
outer.preamble = "You will not see this in a MIME-aware mail reader.\n" # I don't know what I'm doing here, I copied this from SO.
msg = outer.as_string() # Getting the string of the message
_client = details["client"] # Assigning the client
_client.sendmail(SENDER, details["to"], msg) # Sending the mail

没有注释的情况下阅读和理解此代码将变得更加容易,从而使代码混乱且难以阅读。

相反,应将代码内部的注释放在代码块上方,并且它们应回答在阅读代码块时可能出现的重要问题。

# Constructing the email object with the values 
# we received from the parameter of send_mail(details)
outer = MIMEText(details["message"])
outer["To"] = details["to"]
outer["From"] = "xAI No-Reply"
outer["Subject"] = details["subject"]
outer.preamble = "You will not see this in a MIME-aware mail reader.\n"
msg = outer.as_string()

# Sending the mail using the global client (obtained using login())
_client = details["client"]
_client.sendmail(SENDER, details["to"], msg)

更清楚了吧?现在您还知道必须使用该login()函数并为send_mail()使用的所有内容提供参数。有所帮助,但一件事仍然缺少。

功能文件

已经被广泛讨论。您应该始终让读者知道您的功能是什么,原因和作用。如何做到这一点,这不属于文档,而是属于功能的脚注。

您应该清楚地描述期望的参数,以及是否希望以特定方法获得/创建参数。您应该声明函数应该返回什么,其用途是什么,等等。

同样,这是我编写代码时的观点和方法。不仅是这些,而且这些只是我无法同意其他答案的一部分。哦,当然,不仅注释可以读出您的代码,还可以读出您的代码本身。编写清晰的代码,易于理解和维护。考虑编码时的未来自我;-)


5
与给定的示例不同意-与其在一个庞大的函数中编写很多注释,不如从许多具有描述性名称的较小函数组成它,它们将充当注释。无需冒与代码实际操作不同步的风险。
user11153

6
最后有些理智。提取可能在其函数中使用注释的每一段代码,最终将导致成千上万个函数分布在数百个文件中。
user369450

2
第二个例子很可爱。
与莫妮卡(Monica)进行的轻度比赛

7
第二个示例中的注释过于冗长。其中一些(例如“我们找到了什么吗?”)只是重复代码中的内容,最好将其删除。对于其他方法,您可以通过重构(例如使(stream.is_empty())循环条件,或将check_literals的检查移到外部)来提高可读性。
Frax

3
@cpburnz,“我不得不挖掘太多的遗留项目和第三方库,而没有任何代码注释,以欣赏可以解释正在发生的事情和原因的注释。” 一直就是我的观点:这里有注释来解释废话。由于问题是“我该如何编写易于阅读的代码”,因此显然该答案是错误的,因为它侧重于编写注释以解释不良代码,而不是首先编写优质代码。
David Arno

55

IMO最好的文档是您实际上不需要的文档。我也讨厌写文档和评论。

话虽这么说:

  • 选择可读易懂的名字。不要使用nnumberOfItemsFound例如。
  • 不要回避将计算的一部分存储在一个常量变量中,而不是将所有内容都放入一行。
  • 如果您要重用部分任务,则将部分任务从分支移到其自己的(内联)函数中,否则父函数将变得冗长而乏味。
  • 更加详尽,仅在真正需要的地方通过可读性优化代码。

19
这是一个很好的文档度量标准(强制性链接)。
尼尔

4
这也应该在列表上:用代码解释为什么要做自己在做的事情。
t3chb0t

2
对于最后一个项目符号+1,因为过早的优化是(在97%的时间内)万恶之源
gmauch

4
numberOfItemsFound虽然很冗长;太冗长也是一个问题。
Matthieu M.

6
@MatthieuM。,很少会出现“太冗长”的代码名称问题。过于简洁或神秘是一个非常普遍的问题。
大卫·阿诺

25

将您的代码视为文档

您的代码是您的主要文档。它精确地描述了所产生的应用程序,库或实际执行的操作。因此,任何旨在加快对代码的理解的尝试都必须从代码本身开始。

关于如何编写可读代码,有很多文章,但是一些关键点是:

  • 不要依靠注释来解释错误的代码,不要使注释变得更好并摆脱注释,
  • 编写简短的重点功能,方法,类等,
  • 使用适合于上下文的名称(例如,n有利于循环,对于更大范围的项目,则需要更长的描述性名称),
  • 将函数名称视为注释,例如,不要UpdtTbl与注释一起使用,因为注释可以解释使用函数名称来更新表时使用注释UpdateTableContentsWithSuppliedRules
  • 避免变异。每次更改变量的内容时,都会增加代码的复杂性。在可行的情况下,将该新值分配给新变量(具有好名字)。
  • 最后,最重要的是,避免使用“聪明”的代码。唯一真正聪明的代码是易于阅读的代码。如果您编写了一些复杂的代码后发现自己在想“哇,我在这里不是很聪明吗?”,答案几乎肯定是“不,您不是”。

更好地阅读代码

不管代码多么简单,阅读代码都是一种学习的技能。没有人天生擅长阅读代码。这需要练习;很多练习。因此,例如,转到Github或其他地方,阅读您使用的库的代码,而不仅仅是使用这些库。查找要阅读的代码。

注释是代码的味道

只有这样,我们才能获取其他类型的文档。首先,如前所述,避免发表评论。如果遇到包含注释的代码,我会做最坏的准备:代码可能很糟糕,老实说注释也很糟糕。不能通过代码进行良好沟通的人不太可能通过自然语言进行更好的沟通。

当心自动生成的API文档

另外,请注意自动生成的API文档。如果我不得不求助于此类文档,那是因为您的代码很难阅读。再次,使代码简单,我可以直接阅读它。

测试也是文档

测试也是文档。因此,不要将单元测试当作琐事。将它们视为与他人进行交流的方式(您六个月后的自我被包括在此处),以了解代码的工作方式和预期用途。

画图如果有帮助

如果您喜欢UML,那么一定可以找到一个不错的工具,并从您的代码中生成UML图。只是从来没有尝试过使用它来生成代码。它不适合用作设计工具,结果将导致可怕的代码。

有一个“ 1000ft”的查看文档

最后,编写一个概述文档。该应用程序做什么?它是如何做到的?它还连接其他哪些系统?像这样的东西。但是,请勿尝试在此处描述代码结构。让代码做到这一点。让此文档提醒您为什么首先编写代码。


14
我同意您的所有观点,只是评论确实有自己的位置。尽管我同意add 1 to i,注释中没有意义,但注释应能解释代码为什么会这样做。例如,代码if (!something.Exists()) {...}可以用这样的评论: // something exists only when (explanation of the broader scenario)
乔纳森

16
我们都看到了// increment x x++;很多没用的评论,但是将婴儿带出洗澡水并声明评论总是不好是错误的。例如,表单的注释// this case should never happen because xyz throw exception "unreachable"
Angrydust

7
很好的清单。但是就像@乔纳森。我不同意这些评论。有时候,您必须解决第三方框架中的错误。尽管可以将其重构为自己的函数,但仍然需要对为什么需要解决方法(错误编号或错误名称/错误说明)进行一些描述,这仍然是一件很不错的事情。
magu_

16
@DavidArno但是您不能这样做,以解释为什么完成操作。喜欢//XXX: Not using straight-forward method Foo here because ...。这样的注释可能非常有价值,但是由于明显的原因,无法用代码传达这些注释。
cmaster18年

7
我更喜欢它:每次注释都不能很好地表达自己的代码。例如,我在一种方法中有4行注释,解释了第三方错误的解决方法。我没有用代码很好地表达这一点,所以在注释中。我会说它提高了可读性,因为我怀疑有人会喜欢水平滚动以阅读一个非常长且非常描述性的方法名称。“注释是代码的味道”-是的,但是我们必须记住,并不是所有闻到的东西都是臭的。
R. Schmitz

5

提供求职信

除非您是技术性很强的人,否则围绕代码的大多数问题都将不是“如何”,而是“为什么”或“什么”。

这样,减少人们查看代码的麻烦的方法就是写一个简短的描述。这样做的好处是,您可以很容易地编译描述概述,并且更容易访问。(即使是不会/不允许看到代码的人)。

即使是技术人员,求职信也应为他们在哪里寻找东西提供指导。

简单简约要点:

  1. 简介,为什么存在此代码(基本)
  2. 代码子集执行什么功能
  3. 代码在哪里(例如脚本名称)

  1. 这组脚本抓取了StackOverflow并投票赞成Dennis Jaheruddin的答案
  2. 一种。该脚本负责解析html,并分析它是否是正确的用户
  3. 一种。该脚本位于:ScrapeAndVote / RecognizeDennis.scr

1

我通常从构建单独的提交中获得最大的速度提高,每个提交代表编译和工作的中间步骤。

因此,如果我必须向函数中引入新参数才能实现特定功能,那么只有一个提交只会在声明中,定义中以及所有调用位置添加参数,什么都不做。然后,下一次提交将引入功能,而第三次提交将利用新功能的呼叫站点进行更新。

这很容易检查,因为可以快速浏览纯粹的机械变化,然后避开它们。

同样,如果您重新格式化代码,则应该始终是单独的提交。


1

尽管现有答案之间有一个或两个明显的分歧点,但仅是强调,我将尝试总结一下通常的建议,以明确每个人的来历:

  1. 首先,编写干净的代码;之后,其他任何“文档”将自行处理。整洁的代码是一整套需要学习的原则:单一职责类,可以做一件事的简短方法,好的变量和方法名称,通过专注于隐喻而比这些更好的类/类型名(例如,将MultiButtSupporter称为a苏打水),指示需求的单元测试,DRY,SOLID,一致范例等。
  2. 代码揭示了代码的工作方式。注释揭示了代码为什么起作用。例如,用“防止出现1个错误”来解释+1,或者用“从此教科书或网页中得出”来解释一些复杂的公式。
  3. 无论您使用注释做什么,上面的第1点都可以用干净的代码实现。将注释视为失败/必要的弊端,或者甚至随着时间的流逝而撒谎,因为随着时间的流逝,它们与代码不同步,因为两者均被编辑。注释不应该补偿写得不好的代码,因为为什么注释要比代码写得更多才干或谨慎?

另一方面,如果有的话,我可能会反其道而行之,几乎从不使用注释。您的代码审阅者将让您知道平衡点是否在错误的地方,但是,如果您有意识地遵循上述三点计划,那么您无论如何都会接近他们的最佳选择。


2
“防止错误一次出错”注释与“ +1不是拼写错误”或“我不知道程序中的一个错误引起的错误”注释有何不同?(有用的注释通常与源代码中大于+1的内容有关,或与源代码外部的内容有关。)因此,在您的观点2中,“派生自此教科书或网页”仍然是一个有效且实际上很好的示例。然后,您的观点#3似乎暗示您可以使用足够干净的代码来表达“源自此教科书或网页”,而无需添加任何注释;哇,我想看看它在起作用。
Jirka Hanika,

@JirkaHanika也许是一个不好的例子。至于3,我的意思是“每个可能”而不是“每个可能”。所以不,我不认为单独的代码可以澄清这些问题。(好吧,您可以尝试将gaussianFromThisTextbookNamesApproximation用作变量名,但这是个坏主意!)
JG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.