Skip to main content

Notes from the Clean Code book - 4

Chapter 3 (Functions)

To do software is like any other creative process, ideas are structured first and the message latter, until it can be well understood. The first draft may not be organized, but it’s refined and improved until its right.

  • Must be of reduced size (10 lines, max)
  • Maximum nesting of 2 levels, otherwise makes it harder to read
  • Do something or do respond something, not both (changes the state of a struct/object or it returns information about it, making both creates confusion)

Do a single thing

  • MUST do only a single thing, do it right and be the only thing it does
  • Functions that do a single thing can’t be split into sections
  • Functions do a single thing if their instructions are at the same abstraction level
  • The code should read as a text (top to bottom); the first paragraph P0 describes a level of abstraction, the second paragraph P1 details the abstraction of the fist instruction of the previous paragraph (P0) and so on, in such a way that each one describes the abstraction level, whilst it references following paragraphs of the next level.

Use descriptive names

  • The more reduce and concrete a function is, the easier it will be to pick a descriptive name
  • A long descriptive name is better than a short one but cryptic
  • Do NOT be afraid of investing time to pick a good name; try different names and read the code with all of them until one descriptive enough is found
  • It’s not rare that finding a descriptive name ends up in a (favorable) refactor
  • Use the same phrases, nouns and verbs for the modules

Function arguments

  • The ideal number of arguments is 0, later 1 and 2
  • Whenever is possible, avoid having 3 arguments
  • More than 3 arguments require an especial justification and is NOT very common
  • They complicate things from the testing point of view; having to test all value permutations in the arguments is a humongous task, compared to a function with no arguments or a single argument
  • The output arguments are harder to understand than input arguments; before object oriented programming (OOP), output arguments were necessary. They often force to review the function signature, which turns it into a double effort task (cognitive pause)

Monodic form (1 argument)

  • They ask a question about the argument (fileExists(fileName) // verifies if the file exists)
  • They process the argument, transform it into a different thing and returns it (readFile(FileName) // transform the file name into a string of characters)
  • Events; there’s an input argument but not an output argument. Must be careful to make it clear for the reader that it’s about an event
  • AVOID monodic functions that have an output argument, use a return value instead
  • AVOID indicator arguments (boolean); it’s an inadvisable practice, they indicate that the function does more than one thing depending on its value. It is better to decompose the function in 2 (one for a positive value and other for the negative one)

Dyadic functions (2 arguments)

  • These are harder to understand than a monodic function

  • There may not be cohesion between the arguments (i.e: unrelated)

  • Reader has to either stop to understand or ignore one of the arguments, this may lead to possible errors

  • The arguments lack a natural order, for example:

    assert.Equals(expected, actual)
    

    How many times will the arguments be swapped by mistake? There’s not a natural order, but an acquired convention from practice

Triad functions (3 arguments)

  • These are much harder to understand than functions of 2 arguments
  • The problems to sort, ignore or stopping on each arguments doubles

Struct/Object as arguments

  • When a function apparently needs 2 or more arguments, it’s very likely that one of those is included through a new struct or object:

    newCircle( float x, float y, float radius)
    // x and y can create a Point struct
    newCircle( Point center, float radius)
    

List of arguments

  • If the arguments are processed in the same way, they will be equivalent to a list of the data type of the argument; the function could have 1, 2 or maybe 3 arguments, but it would be a mistake to have more

Verbs and keywords

  • The use of correct names for a function improves its explanation and it’s purpose, as well as the order and purpose of its arguments
  • In its monodic form (single argument), the function and its argument must have the form of verb + noun (e.g: process(payment) or better yet processOrder(payment)

No side-effects

  • A side-effect is when the function commits to do a single thing, but it does other things in an obscure or hidden way. This could be cleared up by changing the function name (e.g: validateAndInitializeUser), but this does not meet the rule of doing a single thing.