Functions_101

07 Aug 2019

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

Functions should be small. How small? According to Uncle Bob, the smaller the better. Small functions are straight-forward and easy to understand. They also tend to better follow the single responsibility principle. Conditionals make our functions difficult to decipher, and indent level of a function should never be greater than two.

  • So, how do we know when our function is small enough?

If we can extract another function from it with a function name that is not just restating the implementation that we are replacing, then it is probably worth doing. This is because each statement within each function should be at the same level of abstraction. Mixing levels of abstraction within functions leads to confusing code. Readers are unclear as to which statements are important, and which ones are just minor implementation details.

Another benefit to keeping functions small, is that we can be far more descriptive when naming our functions. The smaller the function, the more likely it is to only do one thing, which enables us to give it a descriptive name that simply states that thing, ultimately leading to improved readability.

Keeping functions small also means limiting our use of arguments. Arguments make our code more difficult to read. The more arguments a function has, the more we have to keep track of. Our levels of abstraction become contrived- especially when we pass arguments from function to function. When we start doing this, it should serve as an indication that we might want to extract that argument into an instance variable.

As we add arguments, testing becomes exponentially more difficult. Monadic and dyadic functions are reasonably manageable. However, thoroughly testing triadic and polyadic functions means testing every combination of arguments including edge cases for each one. This gets out of hand very quickly.

A good solution to triadic and polyadic functions is to create an object out of arguments that can be grouped into a larger concept.

Flag arguments are a huge smell, and passing a boolean into a function should serve as a red flag that the function does more than one thing. It does one thing if the argument is true, and another thing if the argument is false.