Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I can't at all agree with the added differences of opinion with Uncle Bob's Clean Code. The author argues a reducing the size of a function of "a few dozen lines" likely won't improve the readability of the code. That's not even defensible! I would fail any (professional) code review for having that many lines of code in a function. It's a complete failure of abstraction. He later goes on to say "more functions means more interfaces to document and learn." This is also smelly. Clean code is self documenting. It should be simple to read because it should read like prose. If levels of abstraction are kept to one per function, it's easy to understand what is happening. One would only drill down into functions if it were necessary to grok implementation details of an abstract function.

The comment on comments I also disagree with, although that's more contentions. In general I agree with Uncle Bob that comments are usually apologies. Most of the time the code should be refactored to not need comments. Ousterhout does bring up a point that names can sometimes be verbose as a result, but I can't be convinced that's a universal evil in the same way I can't be convinced that comments are a universal evil.

>And, with this approach, developers end up effectively retyping the documentation for a method every time they invoke it!

In what way is that a negative? This is a feature of self documenting code! This is what makes good code so great! This seems like some degree of misunderstanding of Clean Code.

I'm compelled to pick up this book, however.



> The author argues a reducing the size of a function of "a few dozen lines" likely won't improve the readability of the code. That's not even defensible! I would fail any (professional) code review for having that many lines of code in a function. It's a complete failure of abstraction.

Can you elaborate on how this is a failure of abstraction? Or maybe what abstraction means to you?

To me, abstraction is about simplify a problem space by making assumptions about the use case.

A hard disk is a bunch of spinning platters that can store 0s and 1s. The drive controller abstracts that by presenting it as if it’s just one continuous stream of 1s and 0s. The operating system abstracts that and presents it as a file system so you can say “shove this data into this name” then later “give me the data in this name”.

The drive controller simplifies the interface by assuming that “where this goes on the platters” doesn’t matter. The operating system simplifies the interface by making many assumptions about where and how we want to organize this data within that stream. This greatly simplifies the use case of… well, basically everything. Except for the things it doesn’t where the abstraction simply no longer works.

All I’ve ever seen “Clean Coder” style short methods do as far as abstraction is reduce it. Instead of a call tree 12 layers deep to finally abstract a problem away as a single operation, people give up halfway through and leave 6 methods that need to be called to do one conceptual thing, meaning the caller needs to understand the underlying implementation/concerns and there’s no longer _any_ abstraction. This isn’t inherent to that approach, but definitely seems encouraged by it.

Shorter methods may (I’d disagree, but understand) help reusability. But whether your `download(string url, string path)` method is a single hundred line method or is composed from parseUrl, resolveDomain, openTcpConnection, sendData, buildHttpRequest, receiveData, receiveHeader, parseHttpResponse, openFile, writeFileData, closeFile, closeTcpConnection… the abstraction is the same if the interface is the same: retrieve a URL over HTTP and write the body to a file. You don’t care about HTTP, sockets, DNS, URL formatting, or anything else.


> The author argues a reducing the size of a function of "a few dozen lines" likely won't improve the readability of the code. That's not even defensible! I would fail any (professional) code review for having that many lines of code in a function.

I would argue that such absolutist rules are harmful. Yeah, there certainly are times when smaller functions are better. But there are times where separating a function into smaller ones does indeed make it 'harder' for me to read, since with each 'abstraction' you're losing context/details that might be very relevant in the code to follow. I would rather have a 40 line function with the dirty low-level details rather than the same split over 50 lines of different functions that I need to go into and figure out the details.

And then again, who is to decide what is 'one abstraction'? Abstractions can be at different levels, and abstracting different things. There really isn't an objective way to do it. I would argue that this is analogous to writing -- there are all kinds of books/stories/poems, short and long etc. and one isn't necessarily better than another.


I would argue that such absolutist rules are harmful.

Indeed. There are millions of us out there developing software for numerous different applications with numerous different trade-offs. “Never say never” is probably good advice here.

I once had a discussion with a prominent member of the ISO C++ standards committee about the idea of labelled break and continue statements of the kind found in various other languages, which let you affect control in an outer loop from within a nested inner one something like this:

    outer_label:
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < 10; ++j) {
            if (something_interesting_happened) {
                respond_to_interesting_thing();
                break outer_label;
            }
        }
    }
They were essentially arguing that such a language feature should not be necessary because you should never need deep nesting of loops in a program with good coding style anyway.

At that time, I was working on code where a recurring need was to match sometimes quite intricate subgraphs within a large graph structure. This is known as the subgraph isomorphism problem¹ and it’s NP-Complete in the general case, so in practice you rely on heuristics to try to do it as quickly as you can.

That’s a fancy way of saying you write lots of deeply nested loops with lots of guard conditions to exit a particular iteration as quickly as possible if it can’t possibly find the pattern you’re looking for. 5–10 levels of indentation in a function to find matches of a particular subgraph were not unusual. Functions at least 50–100 lines long were common and longer was not rare. It probably broke every rule of thumb the advocates of short functions and shallow nesting have ever written.

To this day, I believe it was probably also the most clear, efficient and maintainable way to write those algorithms in C++ at that time. But it would have been clearer with labelled breaks.

¹ https://en.wikipedia.org/wiki/Subgraph_isomorphism_problem


Good code to me usually comes down to things like state management, code organization, ... Having code that reads like prose isn't a high priority to me, but I'm familiar with that style and I can see why people like it. I just wish they'd realize they're expressing an opinion on style instead of a fact.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: