Clean Code Book – Chapter 3: Functions

Clean Code Chapter 3

This chapter focussed on the writing and naming of functions.

Small

Functions should be kept small, very small.

Functions should not be more than 150 characters long and they should not be 100 lines long. Functions should rarely be longer than 20 lines.

Functions should be transparently obvious about what they do, and they should tell a story.

Blocks and Indenting

Blocks within if, else and while statements should probably only be one line long and that one line should be a function call.

This is beneficial since it keeps the enclosing function small, and also the function will have a descriptive name therefore adding documentary value.

It also implies that functions should not be large enough to hold nested structures. Therefore the indent level of a function should not be greater than one or two. Therefore making functions easier to read.

Do One Thing

Functions should do one thing. They should do it well. They should do it only.

Functions should have only one level of abstraction, if there are more than one level then it indicates that the function can be broken down further into separate smaller functions.

Sections within Functions

There will often be times when a function has several sections within it.

See Lisiting 4-7 on page 71 of the Clean Code Book.

If this is the case then it is obvious that the function is doing more than one thing and should therefore be broken down into separate functions.

Reading Code from Top to Bottom: The Stepdown Rule

Code should read like a top down narrative meaning that every function is followed by another function at the next level of abstraction.

It should read like a list of paragraphs each with one level of abstraction.

See Listing 3-7 of the Clean Code Book.

Descriptive Names

Descriptive function names should be able to tell you exactly what the function does, the code does what you expect it to do based off of the name given.

Assigning good names to a function is easy when the function is small and only does one thing and the code is more focussed.

Don’t be afraid to make a name long. Long descriptive names are better than short enigmatic name and long descriptive names are better than long descriptive comments.

Function Arguments

The ideal number of arguments for a function is zero.

Arguments are not only difficult to conceptualise by the reader, but also difficult to test sine you have to test every possible combination of arguments. And with an increasing number of arguments the testing becomes exponentially more difficult.

Monadic Arguments

There are two main reasons for needing an argument in a function

  1. You are asking a question about the argument e.g. boolean isEmpty(map)
  2. You are operating on that argument, transforming it into something else and returning it e.g. InputStream openFile("MyFile")
Flag Arguments

Flag arguments e.g. boolean should not be used as arguments for functions because it means that the function is no longer doing one thing.

The function performs differently depending on whether the argument is true or false therefore breaking the do one thing only rule.

For example if a function had the signature render(true) it should be refactored into renderSuite() and renderTestSuite() instead.

Dyadic Functions

Functions with two arguments are harder to understand than monadic functions.

But sometimes they can’t be avoided and actually makes sense.

For example, a function called plotPoints(int x, int y) makes sense since cartesian axis always come as a pair and they have a natural ordering.

But most of the time there is no reason to have a dyadic function and alternatives should be considered before using them.

Triadic Functions

Triadic functions are another level of complexity above dyadic functions and should be considered carefully before being used.

Argument Objects

When a function seems to need more than two or three arguments, it is likely that those arguments should be wrapped in their own object

makeCircle(double x, double y, double radius)

This can be abstracted further into:

makeCircle(Point center, double radius)

Verbs and Keywords

Function arguments should form a verb/noun pair e.g. write(name)

An improvement on the assertEquals(expected, actual) function name would be assertExpectedEqualsActual(expected, actual). This explicitly tells you the ordering of the arguments.

Have No Side Effects

Functions with side effects are functions that say they do one thing, but in addition do something else, other hidden things.

For example it may make changes to class or global variables, or perform some operation that is not stated in the function name.

It can result in temporal couplings and order dependencies.

For example, a function called checkPassword() would be expected to just check that a password is valid.

However if this function also initialized the session then this session initialization is a side effect that the caller of the function would not be aware of (unless they read the code) and could therefore accidentally call this method and overwrite an existing session.

This side effect causes a temporal coupling since the checkPassword() can only be called at a certain time (when it is safe to initialize the session).

The session initialization should be removed from the checkPassword() function.

Extract Try/Catch Blocks

The bodies of try catch functions should be extracted into their own functions.

Error handling should be one thing, so if a function handles errors, it should have the try/catch block and that is is. The function should do nothing else.

Don’t duplicate code

Code duplication not only bloats code but leads to more errors. When some of this duplicated code has to change, all of the other duplications have to be updated as well.

It also negatively impacts the readability of the code.

Conclusion

The Clean Code book can be found on Amazon

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *