Why You Should Stop Representing Age As a Number in Your Code
Does the famous saying “Age is just a number” hold true in the programming world too? Can age be simply an integer type? Or is it something more evolved than just a naive primitive type? Can age ever be negative? I bet the primitive integer value can. So what is a better representation of age in programming languages then?

Age and Integer (as much as they look like they were made for each other) are quite different types and have different properties and behaviours. Let's try to think out loud. An integer can be added to or subtracted from another integer, would you need to do that with age? Maybe. An integer can be multiplied with or divided by another integer, would you need to do that with age? Probably Not. An integer can be negative, can age ever be negative? Definitely not.
Just with some initial arguments, we see that Integer has started to diverge from being a quintessential representation of Age. This misrepresentation of richer constructs in programming with primitive types is often referred to as a Primitive Obsession
Primitive Obsession
is using primitive data types to represent domain ideas. For example, we use a String to represent a message, an Integer to represent an amount of money, or a Struct/Dictionary/Hash to represent a specific object.
— https://wiki.c2.com/?PrimitiveObsession
So if Age is not merely an integer representation, then what is it? Let’s explore the idea in more details building on the complexity of representing Age correctly…
NOTE: The code examples listed below use TypeScript. However, the concepts are basic enough to be incorporated in other languages too.
First Cut
Age can be represented in days, months and years, or even a combination of some/all of these.
If we simply used age as an integer variable, how do we get the other devs to know whether the age is in years, months or days? The most naive way I’ve seen this being addressed is through variable naming conventions.
Isn’t it too much work for a dev to keep calculating the values for different representations? Also, we’re way too optimistic that the meaning wouldn’t be lost in translation. Imagine if this age variable was to be passed as parameters to other functions.
The problem with this approach is that it’s too prone to human error and interpretation to be comfortable with. If you’re not already familiar with, read about failure of Mars Climate Orbiter.
So how do we improve further? By building a basic abstraction — a class.
Alright, so we’ve gotten to a good start. Converted a primitive type to a good abstraction with some behaviours/methods to get age in different representations of days, months and years.
We can further improve our code. Remember we talked about the fact that age cannot be negative. But our above code accepts just any number values for years, months and days. So let’s fix that quickly in our constructor:
The above snippet makes sure that our Age object is constructed only with valid and allowed properties.
Final Cut
So far we’ve built upon the basics of a much richer concept called Value Object.
Value Object:
A value object is an object that contains attributes but has no conceptual identity. They should be treated as immutable.
In simpler terms, taking age as an example, the age object doesn’t have any identity of its own. It's generally associated with another entity (an object which can be differentiated from other objects by a unique identification) like a person (every person will have a unique SSN number for e.g.).
The second concept about immutability is what makes value objects even more exciting. If the age changes, the existing object would spit out a new age object itself, leaving the original one untouched. For instance, when we add days/months/years to an age object, it would return a new object with the new attributes, as shown in the code below.
With immutability, your code is more robust against multi-threaded programming because the referential integrity is intact for every object. It might add overhead in certain languages which were not designed keeping immutability as primary constructs, however, the tradeoff at this level is pretty minimal and you’ll almost always benefit in the multi-threaded world.
You may further enrich this model by including the comparison behaviour as well to help compare the two age objects with each other. Combining all the changes that we have made so far, our final Age representation would look like below:
Bonus (update)
Even though I’ve used numbers (years, months and days) to construct the age object (to keep the blog simpler and shorter), effectively age is a difference between two points in time. So it might make more sense to accept a Date/Time object parameter in the constructor to denote the birth date & time(if being used for a person, for e.g.).
Then, the other calculations like getAgeInMonths, getAgeInYears and comparision of ages etc could just be derived from the initial date/time itself.
Thing to watch out for: Remember to take Timezone into consideration along with the date-time object.
Summary
Always explore the richer abstractions aka Value Objects that you can build instead of prematurely obsessing over primitive types. Just like Age, there are several other constructs which are often misrepresented by just using the primitive types. Money is yet another example. Most of the codebases would portray Money as a float or a BigDecimal, completely missing the currency aspect of money.
So next time when you think about a type, conceptualise it using real-world scenarios and following their behaviour in real-world when put to different use cases.
More content at plainenglish.io
Why You Should Stop Representing Age As a Number in Your Code was originally published in JavaScript in Plain English on Medium, where people are continuing the conversation by highlighting and responding to this story.