TL;DR: In 1999, a unit mismatch between Metric and Imperial measurements destroyed NASA’s Mars Climate Orbiter. To prevent silent, logic-breaking failures in your software, you must move beyond generic number types. By using explicit field naming and branded types, you ensure that context is baked directly into your data schema.
In 1999, NASA turned a $125 million spacecraft into expensive space dust. The Mars Climate Orbiter was supposed to gently settle into orbit, but instead, it hit the Martian atmosphere at the wrong angle and was obliterated. It’s gone. It’s not coming back.
This wasn't a failure of complex physics. It was a failure of communication between two teams of literal rocket scientists. One team designed the thruster software using Metric units (Newtons). The other team, providing the input data for those thrusters, used Imperial units (Pounds-Force). Because the interface between these two systems didn't specify the units, the software received the wrong numbers, and the orbiter disintegrated.
It is a brutal reminder that a raw number without a unit is a ticking time bomb.
Why do computers ignore context in unit measurements?
Computers don’t assume context; they process raw floats. If your system expects a value in Newtons and receives a value in Pounds, the CPU will execute that instruction without hesitation because both are just floating-point numbers in memory.
Imagine a microservice responsible for a drone's altitude. If Service A calculates the "safe ceiling" in meters, but Service B interprets that number in feet, the drone will eventually fly into a power line. The logs won't show a crash because the math worked—it just worked on the wrong reality. This is why we can't rely on developers "just knowing" the units.
How should I name API fields to prevent "NASA-style" failures?
The most effective way to prevent unit errors is to bake the unit directly into the field name of your API or database schema. This makes your code self-documenting and forces the consumer to acknowledge the unit every time they write a line of code.
| Ambiguous Field | Explicit Field | Benefit |
|---|---|---|
timeout |
timeout_ms |
Eliminates the "seconds vs. milliseconds" guess. |
file_size |
size_bytes |
Prevents confusion between KB, MB, and GiB. |
price |
price_cents_usd |
Avoids floating-point errors and currency ambiguity. |
thrust |
thrust_newtons |
Directly prevents the Mars Climate Orbiter disaster. |
How do I enforce unit safety at the compiler level?
In languages like TypeScript, a standard type alias (e.g., type Newtons = number) is just a label; it won't stop you from passing Pounds into a Newtons function. To actually raise a flag at compile time, you should use "Branded Types" to create distinct categories for your numbers.
This technique ensures that the compiler treats different units as incompatible types, even though they are both numbers under the hood.
type Newtons = number & { __unit: "Newtons" };
type Pounds = number & { __unit: "Pounds" };
function fireThruster(force: Newtons) {
// This function only accepts Newtons
}
const thrustData = 4.44 as Pounds;
// fireThruster(thrustData); // The compiler will block this call.
Why is documentation insufficient for unit management?
Documentation is where developers go after the system has already crashed. If the unit of measurement is hidden in a README or a Confluence page, it will eventually be ignored during a late-night hotfix or a rushed integration.
By the time you are looking at the docs to see why your data is off, the "orbiter" has already hit the ground. When the unit is part of the API contract—either in the field name or a branded type—it is impossible to ignore. You want to move the knowledge of the unit from the developer's memory into the code itself.
FAQ
Should I always use the Metric system for internal logic?
Standardizing on SI (Metric) units for all internal calculations is a best practice. However, the specific system matters less than consistency; the real danger lies in switching between systems at an API boundary without explicit conversion.
How do I handle units in a legacy system without breaking changes?
If you cannot rename a field, the next best step is to add a new, explicitly named field (e.g., delay_ms) and provide it alongside the old one. Deprecate the original field and log warnings when it is used to encourage a safe migration.
Why are integers preferred for units like currency or time?
Using integers for minor units (like cents or milliseconds) avoids the precision issues inherent in floating-point math. $10.00 becomes 1000 cents, ensuring that addition and subtraction remain exact across all services.
Top comments (0)