代码注释//_您应该停止编写//的五个代码注释,并且//应该开始的一个注释
代码注释// 提供来自您最喜欢和最受欢迎的开源项目的示例-React,Angular,PHP,Pandas等! (With examples from your favorite and most popular open source projects — React, Angular, PHP, Pandas and more!)代码质量与注释之间的相关性 (The correlation..
代码注释//
提供来自您最喜欢和最受欢迎的开源项目的示例-React,Angular,PHP,Pandas等! (With examples from your favorite and most popular open source projects — React, Angular, PHP, Pandas and more!)
代码质量与注释之间的相关性 (The correlation between code quality and comments)
One of the first things we were taught in college was that comments are essential. We were also taught that there is a correlation between code quality and the number of comments a code has — the more comments you have, the better your code is. We were trained to believe that comments tell the story of the program we write, and that they express whatever code can’t provide. We learned that human language is best read by human, while machine language is best read by machines.
在大学里我们被教导的第一件事是评论是必不可少的。 我们还被告知,代码质量与代码的注释数量之间存在相关性-您拥有的注释越多,代码就越好。 我们受过训练,相信注释可以说明我们编写的程序的故事,并且注释可以表达代码无法提供的内容。 我们了解到,人类最好阅读人类语言,而机器最好阅读机器语言。
Moreover, teaching that wasn’t enough and we were “punished” for handing in assignments without comments by a few points being deducted from our grades. If you somehow managed to avoid the human checks, your lack of comments was caught by scripts which were designed to check that.
此外,这还不够,我们在交作业时不加评论就被“惩罚”了,从分数中扣除了几分。 如果您设法以某种方式避免了人工检查,则设计用来检查该脚本的脚本会发现您缺少注释。
也许相关是逆的 (Maybe the correlation is inverse)
As I gained more experience, I realized that not only is it that the opposite may be true — it’s possible that there is an inverse correlation between good code and the number of comments the code has. There are two main reasons why this can happen:
随着我积累了更多的经验,我意识到,不仅相反的情况可能是对的-好的代码与代码的注释数量之间可能存在反相关关系。 发生这种情况的主要原因有两个:
- Too many times comments are just an excuse for writing bad code. Instead of writing good code, programmers believe that if they write dirty code with some weird hacks, and describe it with a 5 lines of comments — that the code is readable and well written. I beg to differ — the code is actually still bad. If your colleague needs to read a long commented story in order to understand it, then you’re doing it wrong. If your code is not self explanatory, it is best to improve it and not use comments to describe it. 注释太多次只是编写错误代码的借口。 程序员认为,如果他们用一些怪异的hacks编写肮脏的代码,并用5行注释来描述它,那么它们就不会编写出色的代码,而是可读且编写得很好。 我要不同的是-代码实际上仍然很糟糕。 如果您的同事需要阅读长篇评论故事才能理解它,那么您做错了。 如果您的代码不能自我解释,则最好对其进行改进,而不要使用注释来描述它。
- Comments decay over time, which makes them wrong and misleading. They are true only when written, and even then they can’t be enforced efficiently. Over time, people will inevitably make logic changes, change types, and move things around. Some of them will notice the comment that should be changed, and some will not. Even if somehow you find a way to set a very rigid discipline around updating comments when code changes, this will break the first time you perform an automatic refactor. Think of a refactor that adds a parameter to a core function that is used more than 250 times — do you really want to go and manually change all those comments? 评论会随着时间的流逝而衰减,从而使它们成为错误和误导性的。 它们只有在编写时才是正确的,即使那样,也无法有效实施。 随着时间的流逝,人们将不可避免地进行逻辑更改,更改类型并四处移动。 他们中有些人会注意到应该更改的评论,而有些人则不会。 即使您以某种方式找到了一种在代码更改时为更新注释设置非常严格的方法的方法,这也会在您第一次执行自动重构时中断。 考虑一下将参数添加到已使用250次以上的核心函数的重构-您是否真的要去手动更改所有这些注释?
您应该避免的最常见评论是什么? (What are the most common comments you should try to avoid?)
All this doesn’t mean you should stop writing comments right away or try to reduce the number of comments you have at any cost. I would also not recommend going over your code, and trying to clean all of the unnecessary or misleading comments — this will take too much time and your time is better used elsewhere. Instead, I would recommend to be more thoughtful before you add your next comment and ask yourself these three questions:
所有这些并不意味着您应该立即停止写评论或尝试不惜一切代价减少评论的数量。 我也不建议检查您的代码,并尝试清除所有不必要的或误导性的注释-这将花费太多时间,您的时间最好在其他地方使用。 相反,我建议您在添加下一条评论并问自己以下三个问题之前,请多加考虑:
- Is this comment really required and what value does it add? 此评论是否确实需要,它增加了什么价值?
- Is there a way to improve the code so this comment is unnecessary? 有没有一种方法可以改进代码,因此不需要此注释?
- Am I only covering my a** by adding this comment? 我只是通过添加此评论来覆盖我的a **吗?
To help you out, I’ve identified the top 5 bad comments I’ve seen over time — these types of comment should raise a red flag before you add it. I used some very common open source projects to get some examples. Don’t get me wrong, I don’t think these project are poorly written. On the contrary, those are my favorite projects. But nothing in life is perfect; all code can be improved.
为了帮助您,我确定了我在一段时间内看到的前5条不良评论-在添加之前,这些类型的评论应该会出现一个红色标记。 我使用了一些非常常见的开源项目来获取示例。 别误会我的意思,我认为这些项目的撰写不正确。 相反,这些是我最喜欢的项目。 但是生活中没有什么是完美的。 所有代码都可以改进。
1.说明明显 (1. Stating the obvious)
These are comments that explain what your code does. You’ve probably seen some of these around:
这些注释说明了您的代码的作用。 您可能已经看到其中一些:
An example from react.js:
来自react.js的示例:
getHasteName() {
// We never want Haste.
return null;
}
And another one from vscode:
vscode中的另一个:
// Avoid Monkey Patches from Application Insights// Avoid Monkey Patches from Application Insights
bootstrap.avoidMonkeyPatchFromAppInsights();
// Enable portable support
bootstrap.configurePortable();
// Enable ASAR support
bootstrap.enableASARSupport();
// Load CLI through AMD loader
require('./bootstrap-amd').load('vs/code/node/cli');
Believe it or not, people reading your code are coders themselves. It is highly probable that they work at the same company as you or on the same project. They have some context, and are pretty smart (hopefully… if you believe you are surrounded by idiots, you might want to consider updating your Linkedin). They can read code, even without footnotes. If your variables, functions, and classes have meaningful names, then don’t clutter them with pointless explanations that will be outdated in the next code change or refactor.
信不信由你,阅读您的代码的人都是程序员。 他们很有可能与您在同一家公司或同一项目中工作。 他们有一定的背景,并且非常聪明(希望……如果您认为自己被白痴所包围,则可能要考虑更新Linkedin)。 即使没有脚注,他们也可以阅读代码。 如果您的变量,函数和类具有有意义的名称,请不要用无意义的解释使它们混乱,这些解释在下一次代码更改或重构时将过时。
Disclaimer: Like many others, I have comment-blindness. I ignore comments and will most likely never notice there was a comment which should be updated when changing or refactoring the code.
免责声明:与其他许多人一样,我也有盲目性。 我会忽略注释,并且很可能永远不会注意到有注释在更改或重构代码时应进行更新。
Back to the example — what happens if we removed all of the comments in the code above? would it really be much harder to read?
回到示例–如果我们删除了上面代码中的所有注释,会发生什么? 真的会更难阅读吗?
2.解释您的代码 (2. Explaining your code)
If your code is clean and uses the right level of abstraction, you don’t need to explain what it does. If you still find yourself explaining the code, it might be the result of some habit you picked up over the years. You might want to consider getting rid of it, or have to endure a code that is not self-expressive
如果您的代码是干净的并且使用了正确的抽象级别,则无需解释它的作用。 如果您仍然发现自己在解释代码,则可能是多年来您养成某种习惯的结果。 您可能要考虑摆脱它,或者必须忍受非自我表达的代码
Look at this code from react.js:
查看来自react.js的这段代码:
if (!existsSync('./scripts/rollup/results.json')) {
// This indicates the build failed previously.
// In that case, there's nothing for the Dangerfile to do.
// Exit early to avoid leaving a redundant (and potentially confusing) PR comment.
process.exit(0);
}
Wouldn’t this be cleaner if we refactored it like this:
如果我们这样重构它,那会不会更干净:
if (buildFailedPreviously())
process.exit(0);
Another common example can be naming; either functions, variables, or classes. Good naming is one of the hardest things to do, but that doesn’t mean we need to unconditionally raise a white flag, and use comments to describe what our code does. Look at this code from php:
另一个常见的例子是命名。 函数,变量或类。 良好的命名是最难的事情之一,但这并不意味着我们需要无条件地举起白旗,并使用注释来描述代码的作用。 从php看这段代码:
struct stack_control_header
{
long shgrow:32; /* Number of times stack has grown. */
long shaseg:32; /* Size of increments to stack. */
long shhwm:32; /* High water mark of stack. */
long shsize:32; /* Current size of stack (all segments). */
};
If you pass it around and then try to use it, you might not immediately understand what shgrow, shaseg and other fields are. What if we wrote it this way:
如果将其传递然后尝试使用,则可能不会立即了解什么是shgrow,shaseg和其他字段。 如果我们这样写:
struct stack_control_header
{
long num_of_time_grown:32;
long size_of_inc:32;
long high_water_mark:32;
long current_size:32;
};
See? Much better. The reader can better understand what each field does without needing to jump to the struct definition and read the comments.
看到? 好多了。 读者可以更好地了解每个字段的作用,而无需跳转到结构定义并阅读注释。
3.长评论 (3. Long comments)
Long comments that are used to describe every decision you’ve made. These comments may explain each line in detail: why you chose to write it that way, what were the alternatives, what is the code history that led to it. It made it really hard to read the code fluently, and it can cause the reader further confusion. Ultimately, causing more damage than good. Try to keep comments as short as you can with minimal context.
长注释用于描述您所做的每个决定。 这些注释可能会详细解释每一行:为什么选择以这种方式编写它,替代方法是什么,导致它的代码历史是什么。 这使得很难流畅地阅读代码,并且可能引起读者进一步的困惑。 最终,造成的伤害大于好处。 尝试在尽可能少的上下文中使评论尽可能简短。
If the reason you add a comments is because the code is hacky or complicated, then make it readable by refactoring it — not by adding another confusing layer. Choose better names, break functions to do one thing, and use abstractions. Whatever you need to make your code more readable, do it with code, not comments.
如果添加注释的原因是由于代码太过乱或太复杂,则可以通过重构代码使其变得可读-而不是添加另一个令人困惑的层。 选择更好的名称,破坏函数以做一件事,并使用抽象。 为了使代码更具可读性,无论需要做什么,都可以使用代码而不是注释来完成。
An example from vue.js:
来自vue.js的示例:
// Async edge case #6566 requires saving the timestamp when event listeners are
// attached. However, calling performance.now() has a perf overhead especially
// if the page has thousands of event listeners. Instead, we take a timestamp
// every time the scheduler flushes and use that for all event listeners
// attached during that flush.
export let currentFlushTimestamp = 0
// Async edge case fix requires storing an event listener's attach timestamp.
let getNow: () => number = Date.now
// Determine what event timestamp the browser is using. Annoyingly, the
// timestamp can either be hi-res (relative to page load) or low-res
// (relative to UNIX epoch), so in order to compare time we have to use the
// same timestamp type when saving the flush timestamp.
if (inBrowser && getNow() > document.createEvent('Event').timeStamp) {
// if the low-res timestamp which is bigger than the event timestamp
// (which is evaluated AFTER) it means the event is using a hi-res timestamp,
// and we need to use the hi-res version for event listeners as well.
getNow = () => performance.now()
}
This will probably require more refactoring to move the focus from comments to the actual code.
这可能需要更多的重构才能将焦点从注释移到实际代码。
4.标题,标题和其他“美化” (4. Titles, headers and other “beautifications”)
Writing pretty code is essential, but that doesn’t mean you should decorate it like a book. We occasionally tend to creates blocks of code and give them titles, in order to differentiate one block from another. Let’s see this example from angular.js:
编写漂亮的代码是必不可少的,但这并不意味着您应该像书一样装饰它。 我们有时会倾向于创建代码块并给它们命名,以区分一个块与另一个块。 让我们从angular.js看这个例子:
...
build: function(config, fn) {
var files = grunt.file.expand(config.src);
// grunt.file.expand might reorder the list of files
// when it is expanding globs, so we use prefix and suffix
// fields to ensure that files are at the start of end of
// the list (primarily for wrapping in an IIFE).
if (config.prefix) {
files = grunt.file.expand(config.prefix).concat(files);
}
if (config.suffix) {
files = files.concat(grunt.file.expand(config.suffix));
}
var styles = config.styles;
var processedStyles;
//concat
var src = files.map(function(filepath) {
return grunt.file.read(filepath);
}).join(grunt.util.normalizelf('\n'));
//process
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
if (styles) {
processedStyles = this.addStyle(processed, styles.css, styles.minify);
processed = processedStyles.js;
if (config.styles.generateCspCssFile) {
grunt.file.write(removeSuffix(config.dest) + '-csp.css', CSP_CSS_HEADER + processedStyles.css);
}
}
//write
grunt.file.write(config.dest, processed);
grunt.log.ok('File ' + config.dest + ' created.');
fn();
...
If you find yourself doing this, your function undoubtedly does more than one thing. It is probably too long, explicit, and lacks some levels of abstractions. In the example above, the function has at least four parts: fetch files, concat, process, and write. Each of these parts appears with detailed implementation, that creates long functions that are also hard to read. This can be fixed by expanding each block to a different function.
如果您发现自己这样做了,那么您的功能无疑会做更多的事情。 它可能太长,太明确,并且缺乏某些抽象层次。 在上面的示例中,该函数至少包含四个部分:获取文件,concat,进程和写入。 这些部分中的每一个都有详细的实现,会创建冗长的功能,这些功能也很难阅读。 可以通过将每个块扩展为不同的功能来解决此问题。
build: function(config, fn) {
files = this.fetch_files(config)
var src = this.concat(files)
var processed = this.process(src)
write(processed, config)
}
As code grows, the “headers” are not bold enough. This is where we get creative and add additional “beautifications” to our comments — line of asterisk, dashes, equals sign, etc. Take a look at this code from pandas:
随着代码的增长,“标题”不够大胆。 这是我们发挥创意的地方,并在注释中添加了其他“美化”功能—星号,破折号,等号等。请看一下pandas的代码:
...
# --------------- #
# dtype access #
# --------------- #
def _ensure_data(values, dtype=None):
...
def _reconstruct_data(values, dtype, original):
...
def _get_hashtable_algo(values):
...
# --------------- #
# top-level algos #
# --------------- #
def match(to_match, values, na_sentinel=-1):
...
def unique(values):
...
def isin(comps, values):
...
# --------------- #
# select n #
# --------------- #
class SelectN(object):
...
class SelectNSeries(SelectN):
...
class SelectNFrame(SelectN):
...
# ------------ #
# searchsorted #
# ------------ #
def searchsorted(arr, value, side="left", sorter=None):
...
# ---- #
# diff #
# ---- #
_diff_special = {
...
}
def diff(arr, n, axis=0):
...
The module includes a list of functions, variables, and classes all mixed together in one bundle with coupled dependencies. This could be avoided using one simple rule — if you feel that you need titles to gather functions or classes together, this would be a good time to break your code to smaller parts.
该模块包括函数,变量和类的列表,这些函数,变量和类全部混合在一起并具有耦合的依赖项。 使用一条简单的规则可以避免这种情况-如果您认为需要标题来将函数或类收集在一起,那么这是将代码分解为较小部分的好时机。
If your class has “groups” of method from different types — each group of functions should be a class of its own. If your file has too many classes or functions that require grouping, it’s time to break each group to its own file.
如果您的类具有不同类型的方法“组”,则每个函数组都应是其自己的类。 如果文件中有太多需要分组的类或函数,那么该将每个分组拆分为自己的文件了。
The code above could be much easier to understand and navigate if we break it to files. By doing this we also decouple the dependencies, so we can import only the code we need:
如果我们将其分解为文件,则上面的代码可能更易于理解和导航。 通过这样做,我们还解耦了依赖关系,因此我们只能导入所需的代码:
date_acces.py:
def _ensure_data(values, dtype=None)
def _reconstruct_data(values, dtype, original)
def _get_hashtable_algo(values):
top_level_algos.py
def match(to_match, values, na_sentinel=-1):
def unique(values):
def isin(comps, values):
selectn.py
class SelectN(object):
selectn_series.py
class SelectNSeries(SelectN):
selectn_frames.py
class SelectNFrame(SelectN):
search_sorted,py
def searchsorted(arr, value, side="left", sorter=None):
diff.py
_diff_special = {
...
}
def diff(arr, n, axis=0):
...
5. / * TODO:* / (5. /* TODO: */)
from react.js:
来自react.js :
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest
module.exports = ReactDOMServer.default || ReactDOMServer;
Whether it’s /* TODO */, #TODO, or <! — TODO →, one thing is for sure — no one will ever do it. Yes, even if you add a name next to it and assign it to someone. The assignee will leave the company long before they’ll fix this issue. I’ve never heard anyone anywhere saying something like: “hey folks, we have some free time, why don’t we fix all of the todos in our code?” (If you have some time for that, then your company has a bigger problems, but we’ll leave that one for another post).
是/ * TODO * /, # TODO还是<! — TODO→,可以肯定的是,没有人会这样做。 是的,即使您在名称旁边添加了一个名称并将其分配给某人。 受让人将在解决此问题之前离开公司。 我从未在任何地方听到有人说过这样的话:“嘿,我们有一些空闲时间,为什么不在代码中修复所有待办事项呢?” (如果您有时间这样做,那么您的公司会遇到更大的问题,但我们会将其留给另一篇文章)。
The main problem with todos is that it’s not only an excuse for writing a bad code, but also it’s unclear to the reader what is the state of that code — Is it going to be changed soon? Was this already fixed and the author forgot to remove the comment? Is there a pull request waiting that should fix this issue? Did the code author leave it for us to fix? — Make a decision, either fix it, or accept the consequences.
todos的主要问题在于,它不仅是编写不良代码的借口,而且读者不清楚该代码的状态是什么—它将很快被更改吗? 这个问题已经解决了吗,作者忘了删除评论? 是否有等待处理的拉取请求可以解决此问题? 代码作者是否将其留给我们修复? —做出决定,或者修正它,或者接受后果。
The one exception is if you are working on a feature and want to break your code changes into multiple commits. In that case, add the todo comment and add your task number/link to a real task in your task management system. This way, you can track it and make sure it is on your roadmap. If for some reason you decided not to handle the task, don’t forget to also delete the comment
一个例外是,如果您正在使用某个功能,并且希望将代码更改分成多个提交。 在这种情况下,请添加待办事项注释,并将您的任务编号/链接添加到任务管理系统中的实际任务。 这样,您可以跟踪它并确保它在您的路线图中。 如果出于某种原因您决定不处理该任务,请不要忘记也删除注释
最后,这是您应该写的评论 (Finally, here are the comments you should write)
A rule of thumb — use comments to answer “Why?” and the code to answer “How?”
经验法则-使用评论回答“为什么?” 以及回答“如何?”的代码
Even if the code is self explanatory, the reason we decided to take one approach is not always clear, especially if the reader has no context. It might be due to product requirements, system limitation, efficiency or just a bad code that you didn’t have time to refactor.
即使代码是自我解释的,我们决定采用一种方法的原因也并不总是很清楚,尤其是在读者没有上下文的情况下。 可能是由于产品要求,系统限制,效率或您没有时间重构的错误代码所致。
Using comments to highlight why you did something the way you did is good, but keep it short and focused. If you want to document, use a wiki; if you want talk broadly about your decision making use a doc; if you want to log the code changes history, that’s what git comments are for.
用注释突出说明为什么您以自己的方式做某件事是好的,但要简短而专心。 如果要记录文档,请使用Wiki。 如果您想使用文档广泛地谈论您的决策; 如果您想记录代码更改历史记录,那就是git注释。
A good example from linux:
linux的一个很好的例子:
/*
Apply the selected BCJ filter. Update *pos and s->pos to match the amount of data that got filtered. NOTE: This is implemented as a switch statement to avoid using function pointers, which could be problematic in the kernel boot code, which must avoid pointers to static data (at least on x86).
*/
static void bcj_apply(struct xz_dec_bcj *s, uint8_t *buf, size_t *pos, size_t size)
If there is one thing you should take from this post — use code to tell your story and comments to turn “WTF ?” to “OHHHH… ?”
如果您应该从这篇文章中学到一件事,请使用代码讲述您的故事和评论以转为“ WTF ?” 到“ OHHHH…”? ”
Thanks for spending a few minutes of your time. If you liked it, feel free to ? or respond with /*comments*/
感谢您花几分钟的时间。 如果您喜欢,请随意? 或回复/ *评论* /
-Alon
-阿隆
Special thanks to:
特别感谢:
Rina Artstain and Keren for proofreading, reviewing this article and giving awesome technical feedback
Rina Artstain 和Keren进行校对,审阅本文并提供了 出色的 技术反馈
代码注释//
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)