Always include units
Whenever I give advice as a software engineer I couch it with a softener: it always depends on the context. Always include units is one of those rare pieces of advice I feel comfortable giving unconditionally (and consistently request while reviewing code).
This applies across variable names, database column names, UI elements and any other place storing otherwise unlabelled numeric values.
—
In practice, being pedantic about naming units correctly has saved me from several gaffes and being off by large factors without realizing it. Of course, these are opinions born of experience; and I'm in distinguished company. Errors like adding seconds and pixels also tend to stand out in the code.
The other valuable payoff I've observed is that consistently naming units is excellent for communication: it makes it significantly easier for someone new to the space to pick up on the code and data piece by piece; particularly if they're only looking at one particular part of a large and complex piece of software.
—
One unfortunately – but unsurprisingly – consistent bug I've observed across codebases is not accounting for the fact that PHP and Python timestamps are in seconds, while javascript timestamps are in milliseconds. These often manifest as timestamps set in 1970: a seconds-based timestamp passed in to a surface that expects milliseconds.
—
The mechanism you do end up relying on to include units does depend on context. I don't have very strong – or well enough informed – opinions here, but there are several patterns I've observed in the wild.
They all have different tradeoffs between simplicity, correctness at runtime or compile-time and potential performance trade-offs. I would recommend that you do any of these, as long as the information about the unit of the numerical quantity is somehow passed along with the quantity itself.
—
At the most basic – and accordingly most portable – level, I recommend relying on consistent naming to identify a variable. For example,
long current_time_ms = System.currentTimeMillis(); // Always do this long current_time = System.currentTimeMillis(); // Never do this
I generally prefer this approach because it costs nothing to add beyond perhaps one extra iteration: and generally receives minimal push-back for adoption. It works directly and consistently across languages and user-interfaces, and has no performance impact.
This becomes particularly relevant where it's not entirely obvious what the quantity being measured is (clearly someone should have consulted me while designing language level APIs):
In javascript:
let current_time_ms = Date.now(); // Always do this let current_time = Date.now(); // Never do this
and python:
import time current_time_s = time.time() # Always do this current_time = time.time() # Never do this
and php:
$current_time_s = time(); // Always do this $current_time = time(); // You get the idea
—
Java has the TimeUnit class which can be passed along with a numeric value. It encapsulates conversions between different units, but doesn't own the value directly.
You end up avoiding the allocation but don't get the encapsulation: it's still possible to ignore the time-unit or find it inconvenient to pass it along.
// From the java docs Lock lock = ...; lock.acquire(50, TimeUnit.SECONDS); // Conversions TimeUnit.SECONDS.toMillis(50); // 50_000
—
Several languages completely encapsulate the quantity and unit in a single class, in a perhaps obvious pattern.
Python has the timedelta object to represent time: they end up encapsulating the time value completely and you don't need to explicitly think about the units. The trade-off would be the extra object being created.
year = timedelta(days=365)
Rust has a Duration class as well, which does something fairly
similar. Rust being rust, I suspect it doesn't cost too much. All the
functions are inlined. I prefer the explicit from_<x>
API for creation
instead of the default constructor for obvious reasons.
use std::time::Duration; let hour = Duration::from_secs(3600);
C++ has std::chrono::duration, based off the same idea, but appears to be much more clever with fractional numbers.
#include <chrono> std::chrono::secs hour(3600);
—
Surprisingly – and happily – enough, CSS does pay a lot of attention to units and using them is surprisingly natural – as long as you do include the units.
height: 50px; width: 98%; font-size: 2em;
—
And of course, sufficiently advanced type systems are indistinguishable from magic. As far as possible, it's valuable to use types to prevent adding ids with quantities, or seconds and kilograms with whatever compile time mechanisms the language affords you.