What this means is that if your Ruby code is clear it should read like natural language. Injecting subtitles into the middle of the expression
# I decided to phrase it this way because I was in a rush and it was the first thing that came out. With more time I could no doubt articulate this in a more communicative way, but for the sake of a random post on the internet I'm not terribly worried.
interrupts the flow when one is reading the code, which makes for a much less pleasant experience. Ruby may not be the only language that is like this, but it is relatively unique in this regard. Comments in other languages don't seem to cause the same interruption.
# That's all I've got. Time to clean up.
I'm not sure this means don't provide additional information to future readers that may be beneficial, but be discriminating in where you put it. Or do whatever you want. Who cares what someone else thinks?
The history of programming is full of that notion and it never really works out. See: COBOL, SQL, and so on.
I think we can all agree that code that is easy to read is better, but sometimes the 'why' needs spelling out.
Better not write tests either, because those will become stale too.
And any sort of user documentation/support/etc. It'll all just become stale.
It's almost as if code is just part of a larger 'thing' that has to be maintained.
But if you are capable of writing perfect tests, why don't you write perfect software in the first place?
Unit tests are easier to maintain when incorporated to a CI/CD pipeline. Then every pushed commit (or Merge Request) will trigger the test suite to run and in case one of the tests fails the team will know.
They do go stale, but tests were invented exactly so that your documentation is able to alert you when it has gone stale. Their existence rests on being an attempt to solve to this problem.
(I'm not arguing against writing comments though - the 'they'll get stale' thing is not enough to make them not valuable)
If you write good tests and use a tool (CI) that detects them going stale, tests will never be stale.
If you write good comments and use a tool (code review) that detects them going stale, comments will never be stale.
In order for tests to not get stale, you need perfect (well written) tests.
In order for comments to not get stale, you need perfect code reviews.
Neither of those exist in real world: code reviews are done by humans, tests are written by humans.
Comments transition from truth to lie without any indication.
Only if you don't do code reviews. The reviewer should be checking for this.
Guess what: Comments are part of the code. Like all other documentation!
If you have a strong discipline of manually checking every possibly relevant comment with each review, I can see how that can mostly work (manual human checks are never perfect).
I've never seen anything close to that process, so it's hard to know how it would work. It seems to require a lot of discipline and effort.
You seem to think that git invented blame. This appeared in previous version control systems, e.g. "cvs annotate" whose synonym is "cvs blame".
I've never been disappointed when digging back in history to figure out why the heck something was done.
It would be a strawman of my position to say that I'm favoring zero comments. For instance, a /* fallthrough */ comment in a C switch statement cannot be in a commit comment, not the least reason for which being that compilers look that that nowadays.
Comments should be like road signs. I need a warning about a dangerous curve ahead, not a discussion of why it was built that way.
Tests represent truth in the same way code represents truth. In many ways the tests represent truth more than the code represents truth. Tests state "this is what I want" and then interrogate the code to verify that the code itself does what I want.
Tests by definition can not be stale (unless they are never called).
The ruby community is where I first learned about test driven development which is philosophically the idea that tests are the highest level of semantic importance.
There are DSL's in ruby land for writing tests like you might write comments: https://semaphoreci.com/community/tutorials/getting-started-...
If tests describe what you want, and code describes how it happens, then the major piece left is why it is the way it is, which almost everyone in this thread agrees is the most important thing to comment.
There is an argument about reducing the amount of work.
On the staleness of comments, it's a real thing. In particular, on comments explaining design decisions, they will often touch on aspects of the system that are outside of the specific method they are attached to, and nobody will go back to them to rewrite all that prose.
We had a project with a ton of documentation written in the first 2 years, and as engineer count grew, these comments just disappeared as again and again they were causing misunderstandings, and actual documentation was already written in the internal wiki as each refactorings and new features were discussed and designed.
It's not just a rails thing, and after a while people stop trusting comments altogether, which make updating them a chore more than anything.
I think that's a reasonable approach if it's easy to find the relevant section of the wiki that describes the code you're looking at by searching. Or just have a comment in the code that points to the wiki. The important thing is to document the "why's".
1. Rewrite your code to make the answer obvious.
2. If that's not possible use a comment.
While it's anecdotal, my understanding is that we were simply working on very, very different codebases. I was refactoring multi-million lines C++ codebases, implementing concurrent algorithms in which dependencies cannot be expressed through language constructs and I had to make non-trivial choices to prioritize performance, or safety, or security at the expense of readability.
On the other hand, these (few) developers were working on writing fresh code, with less footgunny languages, with simpler data and control dependencies and didn't need to make hard choices due to perf/safety/security.
What kind of domain was it?
And this is despite significant and on-going investments in test cases, test infra, integration testing infra, and good overall dev culture around quality.
Also, pair programming has nothing to do with any of this. Mentioning it is like saying that their tests are better because they use IntelliJ instead of Eclipse.
Seriously, PL is all about TDD, I'm not joking. PP actually does matter because two people sign off on commits that they worked on together and it is quite an achievement to convince your coworker that you're sitting next to, to not write a test. Consider it a sort of checks and balances. PL's version of pair programming is 100% of the time, it works.
I have been running an open source project for a few years now with 40k downloads a month on NPM. This project has a heavy dependency on two other projects. Testing isn’t 100%, but it is enough to rely on it for releases. I also started out with TDD as well. Nothing is perfect, in fact we just had a release yesterday with a contributed fix in it, that came with a test, and still ended up with a bug... but out of 118 tags, a few here and there isn't bad. I routinely upgrade all the dependencies and never hear a peep out of people who depend on it.
Most recently, I wrote some software that ran on 20k servers... it was extremely well tested because if it failed, it could have caused massive amounts of downtime.
I've worked for a number of other companies where we had fantastic testing infrastructure, CI/CD... mostly because we started from day one with that. Bolting it on after the fact, never happens to the degree it should.
I just saw Hashicorp release Hermes today... and was saddened to see it had zero testing in it. I just sighed and closed the browser window. It just isn't worth it.
So, just like a proper code review process.
> I have been running an open source project for a few years now with 40k downloads a month on NPM. This project has a heavy dependency on two other projects.
Are they dependencies on libraries, or are they dependencies on services?
Library dependencies are 'easy' to test. Service dependencies are very, very difficult. Tractable, but very difficult. That's just one of the difference between a small project, and a large product.
Just because we have vastly more than two dependencies doesn't mean that I'm working in a shitty environment. It just means I'm working on a large problem.
> I've worked for a number of other companies where we had fantastic testing infrastructure, CI/CD... mostly because we started from day one with that. Bolting it on after the fact, never happens to the degree it should.
I'm not sure why you believe that all of these things weren't with us at day 1, either.
Except it happens in real time, with a real person, sitting next to you.
> Are they dependencies on libraries, or are they dependencies on services?
In this case, other libraries... but I honestly don't see the difference. Libraries and services have APIs. They either work or they don't. Services have the additional complexity of network failures and runtime errors, but these are things that can be dealt with and tested in the primary project.
> Just because we have vastly more than two dependencies doesn't mean that I'm working in a shitty environment.
You made the wrong equation there. The environment is shitty because you said that you're in an environment where people have created products so complex that it is very difficult to achieve adequate testing. You also shrugged off Eclipse vs. IDEA... where I'd actually say that matters. You also talk about disabling tests for security... that seems absurd.
> It's entirely feasible, and highly desirable to get a small project to the point where just running the tests makes you feel 100% confident in the correctness of your changes.
I've built multiple companies that have achieved significant revenue on that confidence. Not small projects. I know it is possible to do.
> I'm not sure why you believe that all of these things weren't with us at day 1, either.
So you started off good and ended poorly? Another reason to believe that the environment is shitty.
I assure you, I am a real person, generally sitting adjacent to people whose code I'm reviewing, or who review my code.
The projects are complex because the business needs are complex. This criticism is the 'I could build Twitter in a weekend, why does it have five thousand people working on it, anyways?'
I, like most people, have plenty of opinionated preferences on the subject of Intellij and Eclipse, but I'm not going to drive any broad conclusions regarding their impact on a product's test coverage. And I will postulate that most attempts to do so reduce down to cargo-culting. It was an example of a largely irrelevant technical decision, and I think it's telling that you are latching onto it.
And it's not disabling tests for security, it's disabling security for tests, as a distressingly common example of a routinely undertested interaction between two complicated spaces.
Not really. The theories around testing are all well established and trodden at this point. People who've been around long enough, have this experience.
> The projects are complex because the business needs are complex.
Complex business needs are separate from the functional testing of individual components. Testing business logic is well... a matter of writing a test, seeing it fail and then writing the business logic (however complex), to make that test pass. Books and PhD's have been written about TDD. The stuff isn't rocket science.
> generally sitting adjacent to people whose code I'm reviewing
Pivotal Pair programming is literally sitting next to the person, sharing a single computer, two mirrored monitors, two keyboards, two mice. Two people independently working together to control a single computer. 8 hours a day (with breaks, of course). Is that what you do?
> I think it's telling that you are latching onto it.
I think it's telling that you dismiss it. Tools are important. It is like telling a car mechanic that every wrench is the same and unimportant. There is no debate on this, IDEA 'eclipsed' Eclipse, especially for Java development (and honestly, everything else), over a decade ago. The final straw for me was when Eclipse crashed for the 1000th time and torched my workspace to the point that I had to delete Eclipse to get it running again.
> it's disabling security for tests
Which says to me that the 'frameworks' used for testing aren't set up well or that the developers you're working with don't have an understanding of or care for how to do that. No thanks. Get it right from the start and make it friendly to the people you're working with so that they aren't so annoyed they have to disable tests. It also says to me that the way things are coded are so intertwined with the security layer that they can't be easily tested independently... which is also a fundamental design issue. Obviously, there are exceptions, but the underlying issue still exists if developers are disabling tests because of this.
> In this case, other libraries... but I honestly don't see the difference. Libraries and services have APIs. They either work or they don't. Services have the additional complexity of network failures and runtime errors, but these are things that can be dealt with and tested in the primary project.
In my personal experience, there are considerable differences.
From the top of my head:
1. Typically, your libraries don't shift under you without any action from your part. External services do.
2. In CI, you can either use the external service (which may require piercing holes in your CI's security and may break due to no change of your part), replicate it (which is more stable but can be extremely painful and resource-consuming) or mock it (which is even more stable but limits the realism of your tests).
3. And of course, as you mention, services have all sorts of failure modes, including but not limited to network failures. Normally, your tests should cover all these failure modes. I don't think I've ever seen code that does that, in part because service errors are very often notoriously under-documented.
YMMV
External services should have versioning, just like libraries.
> 2. ...
They don't need to be 'or'.
> 3. ...
I write tests for this. If I'm using a networking library/function (let's use `fetch` as an example) to talk to a 3rd party external service, I write a wrapper around it to deal with error cases. I then write tests for that wrapper. Anything that uses that wrapper is then inherently tested at the networking level. I then write tests for my own code which uses the wrapper, to deal with the thrown exceptions and how that code should deal with them (retries, throw again, etc).
You don't do that? Do you just assume that all calls to `fetch` succeed?
Mind you, no serious project is anywhere close to 100% coverage. It may have 100% unit-test coverage, but that is a far cry from 100% coverage.
And the valuable comments are the ones that describe integration interactions.
I'm pitching comments as a compromise when the test that encodes their meaning is impractical.
I found that pretty much any methodology works fine in a team like that.
"Don’t comment the code" still sounds pretty crazy to me, but I can believe that it works for them.
/*
The phone number
*/
private static String phoneNumber;
That is considered 'the what', the documentation doesn't explain 'why' the phone number is a field on the class. This might be obvious because the class is called 'Address'... so people will argue that it is self documenting, and it is.The WTF code is 'the why'...
The way is useful in addition to that, and often IME far more important than the what. IMO you should document both, as necessary, and that's the hard part/skill. You have to treat it like you won't remember it in 6 months, because in 6 months, you won't. A lot of devs don't do that.
(A fair number of devs won't follow the style guide / idiomatic patterns of a language if there isn't a CI check forcing them to, and will argue to all ends with a human as to why it ought not apply to them, to the great detriment of their code's readability to other experience practitioners of the language. Oddly CI enforcement usually works better, why I don't know.)
So then the question that needs answering is:
"why" is the what not obvious?
Document the "why."
What is the phone number for? When I read it, is it ready for display as-is or should I be formatting it? Same question when I set it. Can it be null or empty? Does or can the system use this number for anything - such as sending SMS messages?
Being static is a huge red flag, though, and even though it's private I'd still ask. Why is it static? Is this thread-safe (and how)? What context does it get set in, and when can I use it?
In my practice I often see code where useful comments are missing and hours of research required to learn something author for sure knew. Code which has too much comments to me is an imaginary problem - I've seen it at most a couple times in my career and one of them was the code written by a junior developer in a style one can expect in a tutorial or a book. But usually people quickly learn to not add unnecessary comments (and eventually start adding too little of them).
Teaching people how to comment is a skill that's just as important and nothing turns me off a project faster than the "code is its own documentation" mantra.
immediately after composing, for each step and nested step, write a line or two of what its place in the code is for. Write it as though the code is broken and you're following the imaginary line threading through, explained as if to your rubber duck.
Then, having written out the business logic map, look at each written step and see if they're just a description of the logic "iterates through file, passes hits onto nextFunc" and you can safely delete those. They're just glue, really, holding processes together.
What you'll have left is skeletal comments that are restricted to "we did this because this stackoverflow post gave the solution" as well as those mental maps of the solution in your head, which is really what comments are for, future programmers to grok your state of mind and thus better implement their code changes.
Reading comments only tells you what the person who wrote the comment believed. It does not tell you anything in particular about how the system behaved, you have to trust the other human beings (in general a long succession of them) to have understood it correctly. And any one of you can mess that up.
Saying good comments have value is fine. But their ability to lie is unique; code doesn't have that misfeature. You can't make comments lie-free by fiat, for the same reason that you can't train your developers not to write bugs.
Given that, IMHO comments have limited value. Don't be a zealot in either direction, but when debugging hard problems, train yourself to ignore the lying comments.
Code can deceive even if, definitionally, the code states what the computer will do. So code most certainly can lie.
[By analogy, you'd still feel lied to if someone told you something that is technically correct but very misleading: "(Me) It's going to rain tomorrow." → tomorrow comes → "(You) It isn't raining today!" → "(Me) It is raining, in Japan. I didn't say it would rain here."]
I suppose it depends on whether you're considering what the code communicates to the machine or what it communicates to a person.
You can have your pipeline regression test code, but not comments. Just recently my mentee found some of my code where I had changed the code but not the comment. I'm a horrible human and wasted his time. If that comment hadn't existed the code might have taken a moment to understand but as it was, he wasn't sure which was the intent.
Code without comments only gives half the story. It gives you the what, not the why.
Sure, but it can take you on a freaking trip. Comment your code bro.
This whole argument makes me think of C coders who say C is perfectly safe as long as you don’t write bugs.
And to repeat: when debugging, train yourself to ignore the comments. They will absolutely lie to you.
[1] Which, it's important to point out since you used the term "CI", are fundamentally untestable. There's absolutely no way to ensure a comment is correct. A CI smoke test at the very least verifies code builds and doesn't break pre-existing cases. No such validation is even theoretically possible for comments.
It sounds like we have had similar experiences and have come to similar conclusions.
Learning to code, learning to comment and learning to log are all ways to learn to communicate and represent a means to risk-reduction and importantly, to cost-reduction.
Every time I see 0 comment in complex logic I attribute it to laziness of coder. And of course every time excuse is the same - its self-documenting, you see what its doing etc. But why, what are the effects elsewhere in the system and outside, what part of business needs this is covering, what SLA it tries to cover, what are overall goals etc. can be even impossible to grok from just code itself. World is bigger than just code.
function Add(int a, int b) { return a - b; }
Code can lie in so, so many ways that are much more obscure and confusing than this simple example.is even less clear.
You at least know something is strange from the comment.
Comments don't need to describe what the code does, but if there's an unexplicable line which handles an obscure edge case you better add a comment or even you won't remember why that line exists 3 months later.
You wouldn’t leave out-of-date code in a system, it would cause bugs. Why would you leave out-of-date comments? Oh, because you don’t like to write. Your strength is math and code, not writing. Now we get to the heart of the matter and not some ruse like “code is self-documenting.”
there are many standard means that should catch this: code reviews, unit tests, etc.
this stuff can obviously still sneak through, but i don’t think this is a good example of what folks are generally talking about here.
Comment review is as important as code review.
// Add with adjustment factor
function AddWithAdj(int a, int b) { return a + b + 12345 }
When the function was written, everyone probably knew what the adjustment factor was for and how it was determined, but years later, someone's going to look at that code and have no idea.Well, to be fair that adjustment factor could be refactored to use a usefully named constant, with a helpful comment.
static const int ZEN_ADJUSTMENT = 12345; //When I wrote this only myself and God knew what this value meant, now...
int AddWithAdj(int a, int b) { return a + b + ZEND_ADJUSTMENT; }
int AddWithAdj(int a, int b) { return a + b + ZEND_ADJUSTMENT; }
And now the maintainer is going to wonder why the originally programmer defined ZEN_ADJUSTMENT just above this function, but actually used ZEND_ADJUSTMENT (which is apparently defined somewhere else in the code). By design or typo!? :-)Here is another example where a comment can help.
// Do does x, y, and z, in that order.
func Do(x, y, z func()) {
x()
z()
y()
}
Sometimes, as here, the comment correctly describes what the code should do. But the implementation does not agree with the comment. Relying on the comment, a developer can confidently fix the bug, or, at least, notice a discrepancy between the intentions, as described by the comments, and reality, as implemented by the lines of code. This is particularly important in complex code. Think: HTTP keep-alive handling, in which many factors, with varying priorities, have to be taken into account (e.g. client hint in the request header, current server load, overall server configuration, per handler configuration).That’s the problem with comments (and all documentation really) - it gets stale and has no guarantee of being correct.
A better way is to write a test which would break if the order of x,y,z was wrongly changed. This way the order is guaranteed to stay correct forever, regardless of any comments.
That said, I think my toy example wasn't the best way to show some of the points I was getting at. Consider a real example, such as this function from file src/net/http/transport.go in the Go source:
// rewindBody returns a new request with the body rewound.
// It returns req unmodified if the body does not need rewinding.
// rewindBody takes care of closing req.Body when appropriate
// (in all cases except when rewindBody returns req unmodified).
func rewindBody(req *Request) (rewound *Request, err error) {
if req.Body == nil || req.Body == NoBody || (!req.Body.(*readTrackingBody).didRead && !req.Body.(*readTrackingBody).didClose) {
return req, nil // nothing to rewind
}
if !req.Body.(*readTrackingBody).didClose {
req.closeBody()
}
if req.GetBody == nil {
return nil, errCannotRewind
}
body, err := req.GetBody()
if err != nil {
return nil, err
}
newReq := *req
newReq.Body = &readTrackingBody{ReadCloser: body}
return &newReq, nil
}
When you work with code in or around this function, the existence of the comments tell you what the function guarantees, for example, regarding the closing of the request body. If at some point, there is a bug, it is more likely to be noticed because of the discrepancy between the intentions in the comment and the implementation in the code. Without the descriptive comment, an unfamiliar developer working on this code will likely not be able to tell if something is off in the function's implementation.Also, as a user of code, it's nice to first know descriptively what guarantees a complex internal function promises from its comment. Then, if necessary, be able to verify those guarantees by reading the function body.
Of course, in an ideal scenario, a unit test is the best way to address things. But this this is a 22 line function unexported function in at 2900 line file, and sadly it appears this function isn't unit tested.
So, given how things are in practice, I argue that the presence of a comment, like here, can help with correctness of code.
pi = 3You're proving the point of the person you are responding to.
pi = 3 isn't a lie, it's the truth of the program regardless of what any comment says.
The programs that people work with in their mind are not the programs (the code) that the machine runs. What the machine does is definitely more important in the moment, but the models that live in the heads of people are more important in the long term. The code that the machine runs is just one implementation, a shadow cast by the real thing from one of many angles.
Code doesn't tell the full story either. And code can contain bugs so one cannot fully trust the code too.
If code/comments are out of sync it is likely that someone changed the code and forgot to update comments (which is possible to verify using VCS logs) or it may be that the comment stated the intention right but the code has a bug. Code+comments like a parity check to me - if they agree it is good, if not - I have to investigate if the comment or the code is correct.
A long time ago, I had a tendency to write comments to explain code that could be simplified. Refactoring it usually made the comments redundant.
Comments are still very useful when the why is unclear.
That sounds like a nice story
More seriously, I agree with you 100%. Many, many, times I've stopped halfway through a writing an explanatory comment and refactored the to make the comment unnecessary.
This habit was formed by realizing that most people (including me) don't read comments half as carefully as they read code. So if you want something to be noticed and understood, you better put it in the code.
This is true, but if you just get these same people to write more comments, they will also think their comments are clear when they're, in fact, not. I think anyone who is capable of writing clear comments is also equally capable of writing clear code.
If I was to suggest investing time in learning some skill, I would suggest learning how to make your code actually clear by getting the right people (with relevant domain expertise, but without experience with the code) to critique it for clarity rather than learning how to make your comments actually clear.
Why questions are best answered in architecture documentation, the code itself should be self explanatory (given the architecture documentation) for the most part with specific why comments carefully placed in places where it doesn't make sense to use the architecture documentation.
I've read code from various codebases from various companies, large and small. I very rarely see a comment which is genuinely both well placed and useful. From the bits of the linux kernel that I have worked on, I have generally seen pretty good why comments, although I think a lot of them could be shifted into architecture documentation.
All in all, when reading code, my default stance is to ignore comments unless I get lost reading the code, then I attempt to read the comments. It has been incredibly rare that I've found unclear code to have clear comments.
Think of it more like two lines of bearing. You are far more likely to get an accurate position from two lines of bearing. If you need to understand what past self was thinking, a comment is written in a different mindset than the code. Two different lines of reasoning.
Also, I rarely think about writing code for someone else. I write for my future self. And I really want my future self to like my past self. So I strive for clean code and write detailed comments, complete with references.
I mean in theory yes, but in practice I've usually found comments in such situations to either be missing, outdated or misleading.
>If you need to understand what past self was thinking, a comment is written in a different mindset than the code.
I don't think I agree with this at all, why would the mindset be different when they were both written at the same time?
Like I said, writing good comments requires just as much if not maybe more self awareness of what you're likely to forget in the future as writing clear code. I think if you're at that level where you can successfully muster that, you should spend your time just making your code clearer.
In reality I think it's even difficult to muster that requisite introspection skill in the moment and it's usually best left until after you solve the problem (let's say, the next day) to go over the code and check how clear it really is.
Your experience is different than mine: I write software which interacts with cloud (Amazon AWS, Google Compute Platform, VMware vSphere), and a well-placed comment describing the quirks of whatever platform I'm working with helps immeasurably, e.g.
> VMware NSX will throttle us with an HTTP Status 429 "Too Many Requests" if > 100 API calls/second. In that case, we back off and retry rather than fail.
Yes, you're a seasoned HTTP developer. The comment isn't meant for someone like you. It's meant for the other developers on a shared codebase who may not know much beyond the standard HTTP status codes (200, 400, 403, 404, 503).
> taking up more lines to describe the method than execute it
It's a one-line comment. the Ruby code with the exponential backoff, eight lines. It does not take up more lines than the method.
My approach is that conventional knowledge doesn't need to be documented in comments. Attempts at guessing how much conventional knowledge someone is lacking are futile. Those comments are similar to:
int a = 4 // set a equal to four
If someone doesn't know what 429 is, they should get confused, do their own research, and learn. Comments should not be turned into educational material about open standards, they should be about your business. There are much better places to learn about standards.The code comment in the GP is pithy, explanatory, and reads in plain English. Yours is ripe for misunderstanding.
301; 302; 401; 429; 504; I shouldn’t need comments to tell me what they are, nor should it be expected that anyone touching the code knows them off the top of their head.
I don't mean to imply that your code is poorly written as I clearly can't see it and maybe it's a lot more complex than I am imagining it, but I don't really understand how your code was written such that it became unclear enough what code handling a 429 was doing such that you felt you needed a comment to explain it, but I can envision a codebase which handled rate limiting where comments weren't needed to explain the reasoning behind it.
I think the best way to think about it is this: What information is this comment conveying?
- "HTTP Status 429" means "Too Many Requests" - You can put this information in a comment every time you refer to 429, or you can simply use an enum.
- "429 Too Many Requests" means "a rate limit has been hit" - You can put this information in a comment every time you refer to the enum, or maybe it's best placed on a website like MDN[0] where it can be surrounded with far more detail and where the information has a much smaller chance of becoming outdated.
- The rate limit is 100 API calls per second - You can put this information in a comment every time you talk to this particular API, or it's best placed in a document which describes the API itself such that if you ever need to update it, you can update it once. Maybe you can demand that the vendor of the API provides and maintains this document as part of whatever contract you have with them. If necessary, the module of your code which deals with interacting with this API may benefit from a reference to this document.
- That the code should back off and re-try instead of failing - Okay, now we're getting to the core of something important. And I think the reality is more complex than the comment even lets on. Does the code re-try indefinitely? Does it have an option to disable retries in some contexts? I'd assume most likely a no for the first question and possibly a yes for the second. In that case, the names of variables within the code the name of the function, and the names of the parameters of the function should probably be enough to convey this meaning entirely.
That being said, I think that as far as comments go, there is still a benefit of having short descriptive comments for WHAT a function does for every function in a codebase. I've found that they're much more useful (as proper editor tooling can show you them whenever you make use of a function) and by being short and ONLY describing the _what_ and not the _why_ or _how_, the comments have a better survival rate and encourage people to avoid trying to make individual functions too complex.
In this case, your function may benefit from a comment such as:
// Request foo from bar, optionally backing off and re-trying <retries> times
The function signature itself might look something like:
foo(..., retries: Optional[int]) -> ...
Or alternatively you could do something similar with an explicit timeout instead.
The only missing piece I can think of is that if you have some form of back-off you probably need some initial delay before retrying, in this case this should probably be made a constant and this constant (without making any explicit references to its value or whatever value it is based on) may benefit from some reference to the method by which it was chosen. If you do intend to make it depend on the actual rate limit (e.g. 100 requests per second) then that should itself be a constant. But I think generally code like this should be avoided due to its fragility.
In any case, it is again difficult to give too specific of a recommendation given that I haven't seen the code but I don't think the comment you gave is a particularly good example of something I would personally ever recommend or want to see in the middle of a block of code.
[0]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429
Agreed, they "_should_" write the reason why, but many committers unfortunately write the "what" rather than the "why".
I like comments in code, and they're the first thing I read when, say, browsing the kernel or the Golang standard libraries/packages. And I appreciate that you don't find them as useful--more power to you!
Regarding why I don't think that clear code should have comments, it's very simple, comments are not type checked or syntax checked or even tested (unless, I guess, you hire someone to read them). It's very hard to ensure that over time comments don't deteriorate and become unhelpful. This is a much larger maintenance burden than the maintenance of clear code itself.
I think a lot of the issue boils down to people assuming that because all the code they've ever read was unclear, that code itself is inherently unclear.
Imo, first, they are two different skills. And second, sometimes it is easy to write down what I am trying to achieve, but hard to achieve it with clear code. And other times it is vice versa - easy to write the code and hard to put it into words.
The 5 programming languages with the least amount of bugs are:
Haskell
Ada
Scala
Rust
*Drum roll please* Ruby
Ruby is nothing like the other languages above in design, but that community has fantastic conventions around code clarity, code quality, testing, continuous integration, etc.Comments are for things like, `Todos`, Noting a specific algorithm, maybe a link to a white paper, noting a violation of a convention for speed, security, or some other performance reason.
Comments are for things that are __impossible__ to tell from the code.
You can tell everything with code, given enough time and resources. Nothing is "impossible". But there's a point where you hit diminishing returns. It's about efficiency.
Other devs, or your future self, will have to time trying to build the same "castle in your head" as you did. Can a comment shorten that time? If a one-line comment saves 15 minutes of investigation in aggregate, that's a no-brainer. You should add it. Conversely, a line that says "add 5 to x" is just wasting everyone's time.
As a general rule, once I have finally understood a particularly hairy piece of code, I don't want to do it again in 6 months. I have even done ASCII diagrams in the comments explaining how a particularly hairy piece of code worked, when I hated it enough.
It's something I wish was more common than it is. Sometimes you really want that ASCII pseudo graph explaining how minimizing this thing relates to that other thing that we actually care about.
Taken a step further, I really wish we had better systems for managing this type of information alongside a codebase. E.g. the whitepaper, or presentation, or figures, or whatever is often very necessary to understand the code, and ideally it would be under version control and live neatly alongside things. Nicely rendered docs definitely help with this (e.g. latex in comments), but I'm always surprised there aren't more people focusing on this aspect of information management.
I always assumed that behavior was dogmatic until, over many PRs, multiple members of the team commented that if I commented my code, it would be much easier to read. And comments of the above quality assuaged them.
- where the page is being read from
- what page numbers the function accepts
- what happens if an invalid page number is specified (or if that’s deliberately undefined, stating that fact)
- other possible error conditions, if any
(I’ll assume that PageData already documents what “page” refers to in that context, because for me it’s not “self explanatory” just from the function signature you provided.)
That's just not true.
There are infinite many ways to write the exact same thing.
Your code can never say why it's like it is, why this approach was chosen and not another.
> […] the comments explaining how a particularly hairy piece of code worked […]
This kind of comments are the bad ones actually.
Your code should be clear enough that it's easy to see how it works and what is happening.
The rule is actually very simple, and I don't get it why so many people have issues with following it.
"Code says what and how, comments say why."
Why does there have to be rules about when to comment? I comment frequently about whatever the hell I want. Don’t like it? Then don’t read the comments. Very simple, just like your rule.
If you think you're the smartest person on the planet so "rules" (or better said best practices) don't apply to anything you do I hope nobody ever needs to collaborate with you.
All the "rules" are there for a reason!
No, you're not free do to whatever you like in a professional setting. For example when you're operating machines you need to follow safety regulations. Otherwise you could even end up in jail quite quickly.
Except in this case, you've made up the rule. It is not an axiomatic rule like gravity. I want different rules. So unless we work together, your rules don't apply to me.
I agree with that. I don't think that makes things impossible. Only more difficult.
> Your code can never say why it's like it is, why this approach was chosen and not another.
If the why is important, given enough time and resources, someone will eventually complain that something is not working and someone will be tasked to "fix it". It may take them weeks, but they will eventually figure out the why.
> Code says what and how, comments say why.
If we are considering only quality vs quality, I agree. But there's also quantity.
When the how is complicated enough, it is indistinguishable from a why. You will hear the reviewers ask outloud: "But WHY is this done this way?". Even if the problem is merely algorithmic/technical and the code is doing exactly what it's supposed to do.
Then you have two options: spend a potentially significant time refactoring things (which is always the right choice, given enough time and resources) or spend 10 minutes writing a comment with what you learned, and moving on to the next problem.
db.insert(record); // save the record to the database
But when they write some crazy off-the-wall code (that's usually because they didn't understand the proper way to do something) I have to check the logs to see who wrote it and ask them why.
Just the other day I was debugging an application that had a 6 second sleep at the end of the main() function. I just figured it was some dumb thing left in for debugging and deleted it, because there's no good reason to do that. The next day, the dev who put it in messaged me and said he put it there because the application was exiting before it had logged it's completion to our logging system. So I explained that the proper way to do this is to flush the log, not just hang the application for 6 seconds so that the last messages just happen to go through.
If there was a comment explaining why there was a 6 second sleep, I could have just fixed it and educated the developer without causing any grief.
All why answers belong into the code, as comments.
Look up Chesterton’s Fence. :)
(I made similar errors more than once.)
I've seen people get stuck for months being afraid to change a complex piece of code because nobody understands how it works anymore. The best course of action was to admit that knowledge is lost forever and the fastest way to gain it again is to repeat past mistakes.
Of course, don't do this if your software is responsible for landing airplanes. But most of us are not landing airplanes here.
Of course, Chesterton’s Fence is just a guideline. You can weigh the risks against the benefits in each case. It’s just a reminder that there may well be very good reasons why some logic is in place. If it is at all possible to find out those reasons, then that would generally be preferable, in order to make an informed decision.
[0] https://en.wikipedia.org/wiki/G._K._Chesterton#Chesterton's_...
I know, and that's exactly why I don't like it. In my experience, lack of understanding bites you way harder than lack of a single fence.
I once worked at a company that had a very complicated patented (!) algorithm to calculate a certain value. Not a single person in the company knew why it was so complicated, and nobody ever questioned it. We would constantly struggle to introduce new rules to it, because they would conflict with the old rules, and it wasn't clear how to resolve those conflicts.
Since we didn't understand what value those magical rules provided, there was absolutely no way we could resolve conflicts in a way that retains the original value (if there was any).
Eventually I was able to convince everyone and just removed all the rules we didn't understand. Everyone sighed with a relief.
In hindsight, I believe the sole reason algorithm was so complicated was that having a patented algorithm would sound more sexy to investors, and you can't patent something stupidly simple.
Throwaway commenting often makes people feel as if they’re adding value, whilst fulfilling their obligations.
Of course such comments have no value and worse, can distract from what ought to be commented.
Perhaps ChatGPT has found a use, if it is asked to add comments to code, although I somehow doubt it…
> One should take care not to overestimate the impact of language on defects. While the observed relationships are statistically significant, the effects are quite small. Analysis of deviance reveals that language accounts for less than 1% of the total explained deviance
For my code? I agree
Code for others to participate in? The simplest possible way for the greatest number of developers to understand as quickly as possible. Not about my best practice or preference but for the greater good.
Coding standards for the elite devs are only so effective at growing that growing quick enough.
Comments are a critical way to more quickly re-grok the mental model of what's going on for your future self as well.
I think there was a post in the past few weeks about a single developer who maintained hundreds of his own tools, and for him it came down to process and documentation first so it was easy to come back to months or years later.
Many functions in the standard library behave subtly differently from there documentation or have behaviour that is not documented but depended on by applications, and I’ve certainly seen lots of concurrency bugs in both code and tests because the GVL makes it hard to provoke the worst case behaviour.
The unit tests are really useful for Ruby language implementations because they help give us confidence we’re replicating all the required behaviour, but from that point of view I wish the underlying things were better specified so those tests weren’t quite so necessary.
Don’t get me wrong. I love Ruby as a language and loved working on TruffleRuby, but I don’t think the community should be too smug about code quality and testing.
The python folks before me used this to avoid writing docstrings for modules/classes/methods, so code could only be understood by reading the entirety of it. Throw in some deep class hierarchies and it was very hard to onboard there.
If you're relying on comments to help with that, you'll have to share the same mindset as the person writing the comments, except there's probably multiple years of understanding gap between you and them.
if (theWhy()){ theWhat() }
In the simplest cases, sure you don’t need to write “getName returns a user’s first name and last name separated by a space character,” but I’m guessing the method requestPurchase will have a lot of nuance and I hope it’s documented (yes, even the “what happens”)
This would be actually a quite interesting comment as it indicates that the implementation of `getName` is buggy in regard to internalization.
Here's the classic post about this topic:
https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-...
# this is going to loop 4x and call foo_bar on the 4th time because when you calculate this number it needs the 4th time to calculate the difference from the sales tax. weird, I know, but the business has special rules about how taxes work in California # Call foo_bar only on the 4th loop iteration to handle the weirdness of calculating taxes for California.
(I've just joined a company where everyone's constantly writing giant docs, and no one seems to grok the idea of a "living doc" that gets edited and refined. I think it's slowly making me allergic to verbosity.)# Call foo_bar only on the 4th loop iteration to handle the weirdness of calculating taxes for California
# Call foo_bar only on the 4th iteration because in California widget tax # (1st iteration) doesn't apply at all, foo tax (2nd iter.) only applies # for B2B transactions (this is not one), bar tax (3rd iter.) only applies # for alcohol and this subsystem is not called for alcohol sales. But VAT # (4th iteration) does apply.
for i in 0..<4 {
if i == 3 {
foo()
}
else {
foo_bar()
}
}
}I'm not sure I've seen all the "comment everything" advocates in this thread provide a single example of a good comment.
I think there's nearly always value to be added:
* Explaining required fields, if null/blank/0 is allowed, where an ID comes from.
* If a class/method is thread-safe or not (when not obvious and misuse is dangerous), error conditions, timeouts.
* Links to other docs/wikis or tickets is hugely useful.
When you're writing/working on the code you already know all this stuff, and it takes only a couple minutes to document. When someone else (or you, 6 months later) comes along to use or modify it, figuring everything out from scratch can take hours -- or worse, bug reports from QA or customers. Docs shave this down to seconds and directly avoid bugs.
The other huge benefit I often experience is through trying to write docs for something I realize there's a better, more obvious name that makes it easier to use and requires less explanation (less docs). This happens on easily 5-10% of the things I write docs for.
For instance, this [1] is a clean implementation of quicksort, which I offer as an example of any non-trivial algorithm. You can't really write it in a way that would make the idea clear to anybody who wasn't already familiar with the algorithm, because the idea itself is non-trivial. So the way intent is made clear and documented is by making sure the function is called QuickSort. And that is indeed 'self documenting', but really in a way that has nothing to do with the code itself.
[1] - https://www.w3resource.com/csharp-exercises/searching-and-so...
Other times to comment include when there is some sort of dirty hack, TODO annotations, etc.
I'm honestly a bit torn by it. Yes, your code absolutely should be written in a way that keeps it tidy and easy to understand. But you've always got complex situations which arent always going to be easy or obvious for someone fresh to the codebase.
There should be a good middle ground. I dont need to see comments saying "this is a loop that gets all the users". If its got a variables called users, calling a methog called "getUsers()" then thats pretty damn obvious whats happening.
However if you then go on to do something weird like loop over each user and calculate a score based on the number of posts they've made then theres undoubtedly going to be some logic in there that even just a simple one sentence comment will help someone understand.
It's a fine line, and getting it right is a skill in itself.
Obviously, unclear code without comments is better then unclear code with comments.
I worked with some Rails folks a while back who
were utterly convinced that comments were to be
avoided because it meant your code was not clear.
One of the few things that makes me want to reach a management position is my burning desire to alter this widespread, toxic, and absolutely bizarre belief.As an IC, even a senior IC, it's difficult to effect this change.
I agree that comments explaining 'what' is happening are mostly useless. Write clearer code. But 'why' comments are good. Most things can be coded up different ways. Tell me why you chose this way. Tell me about constraints that might not be immediately obvious. Let me know if indeed, it could be cleaner, but you were in a hurry (this is a thing that happens in the real world), or if it's that way for a specific reason.