Explicit declaration of parameters and code evolution

July, 2024

Explicit is better than implicit so says the Zen of Python. This is an insightful statement that Software engineers profess all the time but hardly follow. For example, if the Python language creators really believed this ethos, they wouldn't have provided the default function/class parameter values. I know, I know, it's a nice feature for when we just want to dump a value that we don't want to remember plugging in and still do not want to hardcode into our code implementation.

Default parameter values are our placeholders for when we want to write code that's easily testable and we also envision that there might be scenarios where that value might change in future. It's good software practice to parameterize our function and class definitions.

Sometimes, we write function and class designs that have default values.
However, when the classes/functions are put in use we assume the values for some parameters are already set because of the default values and don't bother explicitly setting the values at call time. This is convenient for the initial usage of our function/classes but it can also easily lead to bugs in future as the code evolves.

The assumptions we used to define the default values might have changed or the class/function usage might have evolved but that function/class argument at its call site will still be using the default value that was set at initial implementation time. For this reason, we need to be explicit about the parameter arguments we use in code. I know it can lead to more verbose code but it saves us future guesses.

An example of code using default values can be seen in the Python code below:
# implementer.py  
def sing(singer, song = "On the low"): 
     print(f"{singer} is singing the song {song}") 


# consumer1.py
import implementer.sing
sing("Burna Boy")


# consumer2.py
import implementer.sing
sing("Taylor Swift")

From the above code, `consumer1.py` might have only wanted to call the `sing` function with just the name of the first singer. However, when a new person joins the codebase and sees how the `sing` function was used in `consumer1.py`, the new person might use it in the same way and end up finding out that Taylor Swift sings the same song as Burna Boy in production.

In order to fix the problem of forgetting to explicitly set the function default value argument at call time, the function should be called like so in all call sites:

# consumer1.py
import implementer.sing
sing("Burna Boy", "On the low")


# consumer2.py
import implementer.sing
sing("Taylor Swift", "I Don't Wanna Live Forever")

In conclusion, I am advocating that we should be more explicit in our code even with default values. Even when we have default values in our function/class initialization, we should endeavor to still pass in the relevant arguments at the function/class call site all the time so that as our class/function evolves, the call site will always be a reflection of the class/function signature.

This article was inspired by a bug I had in production because some of my Python class arguments were default values and were implied at their call sites. As the class evolved, some weird behaviors started showing up in the class output and it took some time before I could debug what the problem was.

As my last words, always remember that explicit is better than implicit.