cat posts/2022-03-22-please-put-units-in-names.md

Please put units in names

· 2 min read · programming craft naming

Quick one, because Ruud van Asseldonk’s Please put units in names went up on Sunday and has been going around since, and I want to add my vote. His examples: time.sleep(300) in Python sleeps for five minutes, Thread.sleep(300) in Java sleeps for 0.3 seconds, and threadDelay 300 in Haskell sleeps for 0.3 milliseconds. Same name, same number, three answers six orders of magnitude apart. He suggests either putting the unit in the name (timeout_seconds) or using a type that carries it (timedelta(seconds=300)), and he means it for config files, metrics, API fields and command-line flags too, not just function arguments.

.NET, where I spent most of my MVP years, manages to be inconsistent with itself. Thread.Sleep takes milliseconds, and to be fair the parameter is literally called millisecondsTimeout, so somebody was trying. SqlCommand.CommandTimeout is in seconds. HttpClient.Timeout is a TimeSpan, which is the right answer, and that’s three conventions in the same framework for the same kind of number. I’ve seen a CommandTimeout = 30000 in production code written by someone who’d just come from a Thread.Sleep, and it worked fine, in the sense that no query ever timed out again, for about eight hours each.

Go gets this half right. time.Duration is a type, so time.Sleep(5 * time.Second) reads perfectly. But a Duration is just an int64 of nanoseconds underneath, and an untyped constant converts quietly, so time.Sleep(300) compiles and sleeps for 300 nanoseconds. Somebody on my team did exactly that in a retry loop in a small Go tool last year and we hammered a partner’s API for a minute before the rate limiter kicked in.

Our worst one was in config. We moved one of our services from a homegrown cache wrapper to a library, and our YAML had cache_ttl: 300. The old wrapper read that as seconds. The new library read it as milliseconds. Nothing failed, every test passed, and the cache just started expiring entries after 0.3 seconds, which is the same as not having a cache. The database noticed before we did, about forty minutes after the deploy, when its CPU went past 90%. The fix was to rename the key to cache_ttl_seconds and have the loader refuse to start if it saw the old name, which is pretty much exactly what Ruud recommends, arrived at the expensive way.

And of course there’s the famous one, the Mars Climate Orbiter in 1999, where one team’s software produced impulse in pound-force seconds and the navigation software expected newton-seconds. The interface spec said metric, one side’s software didn’t follow it, and nothing between them checked, for months of cruise.

The rule I’ve ended up with, for my own code and in reviews, is that a bare number for a time, a size or money never gets past review without a unit in the name or the type. timeout_ms, max_upload_bytes, price_cents. It’s ugly. I’ve had people tell me timeout_ms is redundant because “everybody knows timeouts are in milliseconds”, and I point them at CommandTimeout.