Mirror add units to variable names
Please put units in names¶
or use strong types, to create more intuitive code
source
written byâRuud van Asseldonk
publishedâ20 March, 2022
There is oneâcode readability trap that is easy to avoid once you are aware of it, yet the trap is pervasive: omitting units. Consider the following three snippets in Python, Java, and Haskell:
How long do these programs sleep for? The Python program sleeps for five minutes, the Java program sleeps for 0.3 seconds, and the Haskell program sleeps for 0.3 milliseconds.
How can you tell this from the code? You canât. You just have to know by heart thatâtime.sleep
âtakes seconds, whileâthreadDelay
âtakes microseconds. If you look it up often enough, eventually that knowledge will stick, but how can we keep the code readable even for people who havenât encounteredâtime.sleep
âbefore?
Option 1: put the unit in the name¶
Instead of this:
Do this:
def frobnicate(*, timeout_seconds: int) -> None:
# The * forces the caller to use named arguments
# for all arguments after the *.
...
frobnicate(timeout_seconds=300)
In the first case, we canât even tell at the call site that 300 is a timeout, but even if we knew that, itâs a timeout of 300 what? Milliseconds? Seconds? Martian days? In contrast, the second call site is completely self-explanatory.
Using named arguments is nice for languages that support it, but this is not always a possibility. Even in Python, whereâtime.sleep
âis defined with a single argument namedâsecs
, we canât callâsleep(secs=300)
âdue to implementation reasons. In that case, we can give the value a name instead.
Instead of this:
Do this:
Now the code is unambiguous, and readable without having to consult the documentation.
Option 2: use strong types¶
An alternative to putting the unit in the name, is to use stronger types than integers or floats. For example, we might use a duration type.
Instead of this:
Do this:
def frobnicate(timeout: timedelta) -> None:
...
timeout = timedelta(seconds=300)
frobnicate(timeout)
For a given floating-point number, you need to be told out of band what the unit is to be able to interpret it. If you are lucky this information is in the variable or argument name, but if you are unlucky itâs only specified in the documentation â or not specified at all. But for aâtimedelta
âvalue, there is no ambiguity about how to interpret it, this is part of the type. This also removes the ambiguity from the code.
Scope¶
The advice to use strong types or to put units in names is not limited to variables and function arguments, itâs applicable toâAPIâs,âmetric names, serialization formats, configuration files, command-line flags, etc. And although duration values are the most common case, this advice is not limited to those either, it also applies to monetary amounts, lengths, data sizes, etc.
For example, donât return this:
Return this instead:
Donât design your config file like this:
Accept one of these instead:
And donât design yourâCLIâaccounting app like this:
Accept one of these instead: