Functions — Breaking Down Clean Code Ideas

Danioropezasoria
4 min readMar 20, 2024

--

This article is part of the “Breaking Down Clean Code Ideas” series in which I will go chapter by chapter and section by section, extracting the ideas of the authors of Clean Code in a few sentences or paragraphs.

If you did not see the previous chapter, I invite you to read it by clicking here Meaningful Names.

Chapter 3 - Functions

Functions are the first line of organization in any program. Writing them well is the topic of this chapter.

Functions should be small

Small!

  • Functions should be as small as possible. They should hardly ever be 20 lines long.
  • Blocks within if statements, else statements, while statements, and so on should be one line long. Probably that line should be a function call. The function called can have a nicely descriptive name which adds documentary value.
  • The indent level of a function should not be greater than one or two.

Do One Thing

  • Functions should do one thing. They should do it well. They should do it only.
  • The reason we write functions is to decompose into a set of steps a larger concept. In other words, the name of the function.
  • One way to know that a function is doing more than “one thing” is if you can extract another function from it with a name that is not merely a restatement of its implementation.

One Level of Abstraction per Function

  • Avoid mixing levels of abstraction within a function. It is always confusing.
  • We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions. This is called The Step down Rule.

Switch Statements

  • Try to avoid using switch statements since they are hard to keep small, and by nature, switch statements often do more than one thing.
  • Unfortunately we can’t always avoid switch statements, but we can make sure that each switch statement is buried in a low-level class and is never repeated.
  • The general rule for switch statements is that they can be tolerated if they appear only once, are used to create polymorphic objects (like ABSTRACT FACTORY), and are hidden behind an inheritance relationship so that the rest of the system can’t see them.

Use Descriptive Names

  • The smaller and more focused a function is, the easier it is to choose a descriptive name.
  • Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.
  • Don’t be afraid to spend time choosing a name. Indeed, you should try several different names and read the code with each in place.

Function Arguments

  • The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.
  • Arguments are even harder from a testing point of view. Imagine the difficulty of writing all the test cases to ensure that all the various combinations of arguments work properly.
  • Output arguments are harder to understand than input arguments. Avoid using them.
  • Flag arguments are ugly. Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing.
  • When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own.

Have No Side Effects

  • Make sure your function does not do hidden things. Sometimes it will make unexpected changes to the variables of its own class. Sometimes it will make them to the parameters passed into the function or to system globals. Sometimes it can trigger and event that goes against the business rules and so on.

Command Query Separation

  • Functions should either do something or answer something, but not both. Either your function should change the state of an object, or it should return some information about that object. Doing both often leads to confusion.

Prefer Exceptions to Returning Error Codes

  • Avoid returning error codes from functions. This action creates a problem where the caller must immediately deal with the error using deeply nested if statements. Instead, use exceptions, as they provide their own ‘happy path’ for handling errors.
  • Returning error codes usually implies that there is some class or enum in which all the error codes are defined. Classes like this are a dependency magnet; many other classes must import and use them. Thus, when the Error enum changes, all those other classes need to be recompiled and redeployed.
  • Try/catch blocks are ugly in their own right. They confuse the structure of the code and mix error processing with normal processing. So it is better to extract the bodies of the try and catch blocks out into functions of their own.
  • Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else. So, the try keyword should be the first line of the function and nothing should be after the catch/finally blocks.

Don’t Repeat Yourself

  • Seeing duplicate code that does the same thing is an indication that the code smells. Avoid creating duplicate code and create reusable functions.

Structured Programming

  • In long lines functions, there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements.
  • In short lines functions, those statements do not cause harm.

--

--