Nitpicky Coding Advice: Make Illegal States Unrepresentable and Make Redundant States Share One Representation

September 2026

Back home

I firmly believe this is the number-one rule for clear code.

I'm becoming allergic to code like this:

# BAD EXAMPLE, DO NOT USE
# Every entry has a key.
# Every entry is either a tombstone or a non-tombstone that carries a value.
class Entry:
    key: str
    value: str
    tombstone: bool

The problem is that there are illegal states that can be represented with the Entry datatype:

# BAD EXAMPLE, DO NOT USE
# What does this even mean? Is it a tombstone or not??
e = Entry(
    key="abc",
    value="xyz",
    tombstone=True)

A good guiding principle to avoid this kind of confusion is to make illegal states unrepresentable:

class ValueEntry:
    key: str
    value: str

class TombstoneEntry:
    key: str

Entry = ValueEntry | TombstoneEntry

Now it is obvious from the types alone that (1) tombstones do not carry a value and (2) anything with a value cannot be a tombstone.

You can apply this principle in Python (as above) or Rust:

enum Entry {
    Value { key: String, value: String },
    Tombstone { key: String },
}

or even in modern Java:

public sealed interface Entry permits Entry.Value, Entry.Tombstone {
    public static record Value(String key, String value) {}
    public static record Tombstone(String key) {}
}

A closely related principle is to make redundant states share one representation. In particular, long lists of boolean flags should be highly suspect:

# BAD EXAMPLE, DO NOT USE
class SystemState:
    active: bool
    corrupt: bool # NOTE: ignored when not active

# These two SystemStates are logically equivalent
a = SystemState(active=False, corrupt=False)
b = SystemState(active=False, corrupt=True)

Instead, only the relevant states should be representable:

class SystemState(Enum):
    ACTIVE   = 1
    INACTIVE = 2
    CORRUPT  = 3

Too often I've seen documentation used as a crutch for these principles. One can get away with myriad sins by carefully documenting the corner cases and the meaning of each data type and field. On the other hand, following these principles can save you a lot of documentation effort by making the types themselves document which states are legal.

These principles are closely related to the pattern of using dataflow and types for preconditions: having a named type for, say, UpperCaseString makes the legal representations obvious in code.

Back home