Enumeration - Best Practices

January 10, 2018
enumeration programming project

The Dream

Dear fellow developers, let me propose a big idea.

In the age of multi-repository and multi-language projects, it should be easy to create, understand, and manage enumerations and constants in a platform-independent manner.

This is the end goal of my project, Enumerate.js.

Differences of opinion have prevented this from happening in our world, but I am confident that this is possible.

First, let me explain the issue with an example.

The Problem

Bob, Jane, Carl, Linda and Jim work at Beautiful Software Incorporated. They are working together on a multi-repository project and a simple database of User objects. Here is their story.

One day, Bob decides to add an enumerated field called user_type to their User database. Bob decides that the number 42 will represent a user with a type of Client.

Jane uses Bob’s documentation and commits this code into repository #2.

if (user.user_type == 42) {...}

Carl is not happy about this. Carl does not know what 42 means. Carl decides to edit Jane’s code.

if (user.user_type == 42) {...}  // WTF is '42'?!?!

Linda knows how to solve this. She edit’s the code like this.

USER_TYPES = {
  EMPLOYEE: 42,
  ...
}

if (user.user_type == USER_TYPES.EMPLOYEE) {...}

Now Jim is angry. He does not want to import USER_TYPES everywhere, and is worried that USER_TYPES will become out of sync with repository #1.

Surely there must be a better way than this…

The Principles

All developers have ran into disagreements like these. Now, I would like to propose some principles for enumerations to solve these issues.

1. Constants should be human readable

While integer-based enumerations have good performance implications, they are difficult to read. By contrast, string-based enumerations are much easier to read.

In the modern age of fast computers, the need for human readability almost always outweighs the cost of performance.

if (user.user_type == 'employee') {...}  ✓ Good - I can understand this code easily
if (user.user_type == 2) {...}  x Bad - Not readable - what is '2'?

2. Constants should be globally unique for the entire project

To avoid ambiguity, all projects and enumerations should have uniquely identifiable constants.

x Bad - Two enumerations have the same constants 'foo' and 'bar'
Enum1 = {'foo', 'bar', 'qux'}
Enum2 = {'foo', 'bar'}

✓ Good - The prefixes 'e1_' and 'e2_' make constants unique
Enum1 = {'e1_foo', 'e1_bar', 'e1_qux'}
Enum2 = {'e1_foo', 'e1_bar'}

3. Conditional statements with enumerations are not future proof

✓ Good - future proof and readable
DATA_MAP = {
   'employee': 'a',
   'user': 'b',
   'other': 'c',
}
print DATA_MAP[user.user_type]

x Bad
if (user.user_type == 'employee') print 'a'
if (user.user_type == 'user') print 'b'
if (user.user_type == 'other') print 'c'

4. Enumeration maps should be platform independent and serializable

It should be easy to transfer an enumeration from one repository to another. Application-logic specific code should be ran sometime after the first definition.

USER_MAP = {  x Bad - 'Date' and `MyClass1` are programming language specific
   'employee': Date(1, 2, 3),
   'employee': MyClass1,
}

5. It should be easy to synchronize constants over multiple projects

If a developer adds a new constant to an enumeration, it should be easy and simple to transfer those changes to all related projects.