Hacker News new | ask | show | jobs
by hcarvalhoalves 1832 days ago
IMO, if I feel the need to add comments to explain code blocks like this:

    > // Add a horizontal scroll bar
    > hScrollBar = new JScrollBar(scrollBar, HORIZONTAL);
    > add(hScrollBar, BorderLayout.SOUTH);
It's a sign I should be writing code that reads more like:

    > addHorizontalScrollBar()
5 comments

Meh. If you're only doing it once, then I'd argue that the main practical difference between

  // Add a horizontal scroll bar
  hScrollBar = new JScrollBar(scrollBar, HORIZONTAL);
  add(hScrollBar, BorderLayout.SOUTH);
and (assuming scrollBar is not a local variable)

  void addHorizontalScrollBar() {
      hScrollBar = new JScrollBar(scrollBar, HORIZONTAL);
      add(hScrollBar, BorderLayout.SOUTH);
  }

  ...

  addHorizontalScrollBar();
is whether you want to do it in 3 lines or 5.

(edit: disclaimer -- Snark aside, I should say that I personally tend toward the latter option. But I am absolutely not prepared to try and convince anyone that it is, in any objective sense, better. It's just an aesthetic preference. Maybe even a nervous habit.)

I think this is a question of level of abstraction.

I would argue that that this more readable as for what is going on:

    addHorizontalScrollbar() ;
    addVerticalScrollbar();
    addHeader();
    addDayColums();
    addFooter();
If all I need to know is how the page is structured and set up on a high level, then this is awesome (actually I'm not convinced it needs the scroll bar stuff on this level but this was what I could come up with to stay in the example :) ) I don't need to read comments for what's what and then manually skip over ever so many lines of actual code that did all these things. I can decide that what I really wanted to take a closer look at was what the header bar looks like.

Some of this depends on whether I'm using an IDE or not. In an IDE I can just Ctrl click myself through. Then again I doubt anyone these days is using _just_ vi to code something complex enough to want multiple files. I used vi configured as an IDE way back but I do admit that's been like 15 years. Dunno what that looks like today. Nowadays I'm a JetBrains user, mainly in Java and Java/TypeScript and very little Python.

This might be more readable if setting up those things doesn't involve a set of common variables (say a GUI style in this example) that can affect them. If it does, then you have to pass them around (into these functions) and it makes more difficult to figure out, if one of these variables changes, what all is affected by it (you have to look up all those methods).

In short, I don't think adding depth to a hierarchy of function composition is always an answer. Flatter hierarchies can be easier to understand too.

I think it makes the source of such a styling bug/flow of what's affected a lot more obvious than having to find the reference in each line of the implementation details:

  addHorizontalScrollbar($style);
  addVerticalScrollbar($style);
  addHeader();
  addDayColums($style);
  addFooter($style);
>setting up those things doesn't involve a set of common variables

So...object methods?

So what with them? I don't see how they solve the problem.

What you seem to suggest is refactor the original code (which presumably was a single function to set everything up) so that variables used and the substeps are in one module (your new object), and the code that uses all of this is in another module. I am not convinced this really follows "high cohesion, low coupling" dogma, since you put together things that might be less related (different style variables) into the same module, and created a function, which almost all it does is calling into another module, and is highly dependent on it.

The problem ultimately is data relationships in the code can be a general graph, and trying to put a general graph into a hierarchical structure (much less one that actually has to follow the structure of operations on the data) always has to break cycles in some way. And how much you can really do it depends on cyclomatic number of the graph, and if it's high, no strategy is going to be good. AFAICT most of the OOP strategies of "dealing" with this problem are just creating more nodes in the graph, making the structure bigger, but not really reducing the cyclomatic number.

and that's how we created $company_framework/lib, I guess?
The whole thing should be condensed to:

    add(new JScrollBar(scrollBar, HORIZONTAL), BorderLayout.SOUTH);
It's self-evident and needs no comment or whitespace to "break it up". This is fundamentally a problem with wordy imperative languages.

A lifetime ago when I wrote assembly code I would write a comment like this to explain every subsequent 5-10 lines of opaque incantation. Higher level (especially functional) languages tend to have fewer issues like this.

Unlike the original, this doesn't retain a reference to the scrollbar object.
I'd say it's the enforcement. It's like how YARD docs convey the same information as built-in compile-time type declarations, except you can't trust them. So you start ignoring them, forget to update them yourself, and kick off a vicious cycle.
This is an often used example in defense of not writing comments but in practice I rarely have encountered an out of date or misleading inline comment. In my experience, everything in code tends to stay up-to-date because it is often written and reviewed by multiple people who have some incentive to keep it correct. It's other documentation outside of code that frequently goes stale.
Also, function names sometimes get out of date too, 'addHorizontalScrollBar()' in the example above -- let's say someone adds vertical scroll too but forgets to update the function name.
Having had a lot of experience myself, I sadly can't echo this. A lot of it has to do with stick-and-carrot incentivization, stack ranking, and other stuff.

Unfortunately every place I've worked at sort of accidentally pits their programmers against each other in a sort of "who can get it done first" competition, with those who do a quick-and-dirty job getting credit, and those who clean up their proverbial workbench getting none. We've had gentlemen's agreements to not do this, and have even been able to collectively oust (i.e. get fired) a few serial violators, but there's just SUCH pressure to ship quickly that code review is basically a fictional thing like the flying spaghetti monster.

It's telling that we've got a MLOC codebase, and basically not a single piece of documentation - and every single place I've worked, in almost 2 decades, has been exactly like this.

Enterprise software is a wild ride. :|

I would not agree to work in a place like you describe if they doubled my current salary.

I really hope these companies compensated you well...

Consider yourself lucky then. I've wasted tons of time because of them.
Consider reviewing of your code review process.
Code review can miss because the inaccurate comments might not be close enough to show up in the diff viewer

Generally if I see something happen in every job I've had throughout my career I start to doubt that there is a simple process solution.

I guess I have been lucky but I don't really get this. You read comments along with the actual code right? They complement one another. An out of date comment should not ipso facto render code indecipherable and if it does that speaks to a bigger problem with the code itself.

I find it strange that people want to throw the baby out with the bathwater when it comes to commenting code just cause they've been bit before.

You don’t see how you could end up wasting time when a comment led to you making an incorrect assumption? If they don’t affect how you read the code would mean they’re useless.
But now you can also easily test it independently of whatever else is there if required, and the 'section heading' comment (now function name) is much less likely to go out of sync with the actual code, or accidentally adopt unrelated GUI setup that should have been in a different 'section'.
This sounds perfectly reasonable in isolated examples, but I find maintaining large applications written in this style to be a nightmare. Once you get to non-trivial examples you'll have a lot of variable being passed around (what are we adding the scrollbar too) and stepping through a million little functions to see which components are interacting and how.

The first example might need a comment, but everything I need to know is right there in one place and not distributed across the codebase.

If you're building a UI frame, like in this Swing example, I think it's reasonable to keep the code in a single method and separate it into functional blocks with comments. But the variable names and comments should still include a bit more of the why. Example:

    // Build the data table view
    ...

    // Add horizontal scroll bar to the data table view
    // This is needed if the table has more than N columns
    tableHScroll = // etc

    // Build the data navigation sidebar
    ...
As an aside, I wish web UI programming was more like a declarative form of Swing (that is, a widget- and layout-oriented tree), rather than the current mess of React and a text-/document-oriented tree.
Think about this. You intentionally left out the very verbose parts of actually doing those things for us to see what your UI looks like on an abstract level here on HN. You basically only gave us the comments and left out the 49 lines of actual code needed in between for each ...

With methods extracted you could do the exact same but on actual code and help your future self that forgot all about this class or some other poor developer that has never seen this UI or the code before.

I think my problem with this approach and why I lean to favouring the more obvious and verbose comment is, if I'm reading the code it's probably because something is broken (or I need to change something in the area).

With the function I think, "is it and if it is what else does it change?". Is that function definition actually doing what it says it is? So now I have to add another stack frame to my mental model, go to definition on the function, confirm that is indeed what it claims to do, pop the frame and resume my reading of the parent method.

With the comment I get the statement of intent and can see if the code matches inline. Now it's fair to say a good code review culture (and immutability) might create code where there's more room to trust named functions, but I've not seen it yet.

I don't really get why your mental model is bothered more by the function/stack frame than the comment.

Whether I read a function name or comment personally that does pre occupy my mind and sets the mental frame for what comes next. If the comment or function name say doX() or //this does X then I expect it to do that but need to be on the lookout for whether it actually does that.

With a function though I can easily step out if this part turns out not to be interesting, I can easily step over it next round if debugging, I don't need to keep track of where //this does X ends etc.

Of course none of these help if the previous author was really bad at what they were trying to communicate. They can both implement doY() in a function called doX() and have //do Y code run in with //do X for example by interleaving statements for both. If we assume we'll intentioned authors for both though, I would tend to see more pros for structuring code with functions than via comments.

Stepping in and out is an extra step, mentally and physically. It's increased burden on my working memory, which is usually already quite full keeping track of other aspects of the task at hand.

Also it imposes more burden on the author, and possibly could lead to developing the wrong abstraction, which we all know is bad.

I don't get that. If you need to step over a function call you just remember 'oh right, step over doZ()' vs 'oh right this is 78 lines in which the code does Z, and dang I'm stepped 24 lines into it already before noticing. Where was the end of this again so I can skip ahead?'

One of those seems easier to me. Potentially our brains just work way differently but I have a much easier time remembering that function name I will step over.

I think this probably relates to fundamental differences in how people think. Like the "imagine an apple" test of people's mental modes of imagination.

I'm quite a visual thinker so I use the shape of the surrounding code to anchor my mental model of the code. If I have to jump to another file or function then I lose the shape and it disrupts my model. This is why on balance I (now) prefer longer functions over code split up.

Do you then document the new function, though?
You've heard the XTreme slogan "Where you would write a comment, we would write a method"?
I think the function name is pretty self documenting, I would go a step further and make a function named addScrollBars() clear on its intention here
Sure. Agreed. But I still docblock every function I write so this wouldn’t change much with respect to usefulness of the documentation.