The simplest one rule to write good functions

Lorenzo Peppoloni
3 min readJun 26, 2018

--

Since I started to improve my software engineer skills, one thing that always puzzled me is: how to write a good function.

The usual comment you hear about writing good functions is:

A function should do one thing and one thing only (possibly it should do it right).

I have always found this comment kind of counterintuitive: How do you define one thing?

Suppose you are writing a function to import some variables from a file into a structure (basically an unmarshalling function of some sort). The function is doing one thing: parsing variables from a file…So do you need only one function? Do you need to split the behaviour you want in smaller units and write more functions? And in that case, what is the atomic unit you should choose?

Even with a simple example, we are immediately in front of a choice to make, the rule by itself does not tell you what is the approach you should take.

Another different rule you can find from different sources is:

The statements within one function should be all at the same level of abstraction.

Now, this I think makes more sense!

An abstraction is a representation of something. That something can be represented using different levels of abstraction. The key idea of an is that the entity in question is distinct from the representation at a lower level of abstraction.
As an example, bits are usually the lowest level of representation, then there are bytes, then memory locations and CPU registers. Over them, we have primitive data types and instructions, that are implemented in terms of the lower levels. The important point is that when you interact with a level you do not care about the lower ones. We generalise more and more at each step, thus we only focus on details of interest.

Let’s keep that in mind and go back to our example.
To import some variables from a file, you open the file then you read the content, then you parse the content and write the parsed content in your data structure. So according to the previous definitions, Opening a file, parsing the content and assigning the parsed content live in the same abstraction level. How you open a file and how you parse the content (and possibly how you store the data in the data structure) are at a lower level of abstraction. Thus, they should live in separated different functions, which are called by the higher level.

That was quite of a long reasoning for a simple (almost trivial) functionality.
If we have a look at how I phrased the different the different functions for the different levels of abstraction, it will be clear instantly what is a very simple and general rule to be sure that you write your function in a clean way.
In particular, I wrote:

To import some variables from a file, you open then you read the content, then you parse the content and write the parsed content in your data structure.

That’s the highest level of abstraction. We can iterate the same format for the lower levels.

To open a file you…
To read the content you…
To parse the content you…

Do you see the pattern?

The program should be written as a set of paragraphs starting with TO, each paragraph describes the current level of abstraction and it references to subsequent TO-paragraphs at the lower level.

I first read about this trick in Clean Code and I found it to be the most straightforward rule to follow when writing my own functions, without having to think in terms of abstraction levels or “doing one thing”.
Implementing this reasoning while writing your own code will take care of keeping functions short and making sure they do exactly one thing.

--

--

Lorenzo Peppoloni

Tech enthusiast, life-long learner, with a PhD in Robotics. I write about my day to day experience in Software and Data Engineering.