Clean Code – Understanding Function Steps in Clean Code

clean codefunctionsjava

This doubt is about function doing one thing from Chapter 3: Functions of the book named Clean Code

Here Uncle Bob is talking about this function:

public static String renderPageWithSetupsAndTeardowns(
  PageData pageData, boolean isSuite) throws Exception{
  if (isTestPage(pageData))
    includeSetupAndTeardownPages(pageData, isSuite);
  return pageData.getHtml();
}

In this function I can see, it's doing three different things:

  • Determining whether the page is a test page.
  • If so, including setups and teardowns.
  • Rendering the page in HTML.

But Uncle Bob says:

If a function does only those steps that are one level below the stated name of function, then the function is doing one thing.

What does that means? How does this statement proves that the above function is doing one thing?

Best Answer

The fact that you were able to so easily make that list of bullet points is of relevance here :)

This question might be considered opinion based, but this is about levels of abstraction. The concept may appear too academic, almost intimidating, but it just refers to the level of detail with which you express things, and the choice of particular detail you include - and this depends on who are you talking to and why.

E.g., in an informal scenario, if someone asks you "When will you be available?", there's a difference between you answering "I'll get back to you in a week." and "Oh, man, I'm traveling, I'm going to this rock concert, the stage is within this medieval fortress, and I've heard so many good things from people, it's going to be such a blast, you're never going to believe who's playing [etc., etc.]!". In the second case, the person who asked the question might be able to extract the information relevant to them eventually, but the first level of abstraction is likely preferred - e.g., in a business scenario (where the person is busy and they just want to understand when you'll be available). Similarly, if someone asks you "Where are you going?", but from the context, it's clear to you that they aren't interested in all the detail, you could just say "To a rock concert in Hungary" - thus making a different choice of the relevant details.

In software, you're expressing behavior using functions, and you're faced with similar choices, creating conceptual levels of abstraction. This is in part for the benefit of the readers of your code (other programmers, or future you), and in part for organization and maintainability. So, the exact natures of "level of abstraction", and "one thing", or "single responsibility" are necessarily somewhat up to you and depend on the particular problem you're trying to solve with your program. You try to identify different axes of change and divide responsibilities according to that, on a multiple hierarchical levels of abstraction (you may need to continue to refactor towards this as you work on the project - this is not something you're going to get completely right from the very start).

Now to the core of your question:

In this function I can see, it doing three different things:

  • Determining whether the page is a test page.
  • If so, including setups and teardowns.
  • Rendering the page in HTML.

What this function is doing is orchestrating those other functions which are one level of abstraction below. Its job is to put them together and decide what gets called when. Those other functions are doing the actual, individual things.

The same idea applies recursively, within those lower-level functions as well.

But Uncle Bob says:
"If a function does only those steps that are one level below the stated name of function, then the function is doing one thing."

I'm pretty sure that's what he means here. As a rule of thumb, if a function is only orchestrating functions that are one abstraction level below it, then there's a good chance it's doing a single thing in this sense. Also, the code is more declarative - you can almost read it as a sentence, as you've demonstrated by creating your list of bullet points to which it maps almost 1-to-1. And it doesn't dig down into the lower level concerns that are not its business. Otherwise you'd have to mentally parse a bunch of if-s or loops and strangely named variables to even figure out what is the high-level thing the function is trying to do - this scenario corresponds to the situation described at the start, where a blabbering answer is given to a higher-level question.

For the most part, when someone looks inside a function, they generally want to know what's it for, what its job. It's a high level question. Be deliberate about levels of abstraction and choose good names, and refactor to make sure that they can just read the answer off of your implementation. Don't settle for blabbering functions; your readers should be able to make a sensible bulleted list, just like you have done. If they need more detail, they can dig in, and if your names are good, they'll know there to look.

On the next page, Uncle Bob says: "We want the code to read like a top-down narrative". This is, more or less, the idea he's describing there.