Consistency matters

Published on Feb 10, 2022

Consistency is important for organizing your life or leading your business to success, or so some say. Though, that’s not what we’re going to talk about this time.

Consistency is also an important quality of any complex enough system. Unfortunately, sometimes you may find it being neglected, and dealing with such a system becomes not as pleasant as it could be. As I spend a significant portion of my free time on developing software (and releasing mostly nothing, despite that), naturally the examples I’m about to give will be related to this field, but I’m sure the same point applies everywhere.

Let’s start with something simple: xdg-shell, a Wayland protocol which defines various objects to create a desktop environment. In a perfect world, all programs connecting to the compositor behave exactly like the protocol tells them to. Of course, that’s not always the case, so a bunch of error enums are defined for you to send something to those naughty clients. Here’s an enum for xdg_wm_base:

<enum name="error">
	<entry name="role" value="0" summary="given wl_surface has another role"/>
	<entry name="defunct_surfaces" value="1"
	     summary="xdg_wm_base was destroyed before children"/>
	<entry name="not_the_topmost_popup" value="2"
	     summary="the client tried to map or destroy a non-topmost popup"/>
	<entry name="invalid_popup_parent" value="3"
	     summary="the client specified an invalid popup parent surface"/>
	<entry name="invalid_surface_state" value="4"
	     summary="the client provided an invalid surface state"/>
	<entry name="invalid_positioner" value="5"
	     summary="the client provided an invalid positioner"/>

And here’s an enum for xdg_positioner, a small one, as not many things can go wrong with it:

<enum name="error">
	<entry name="invalid_input" value="0" summary="invalid input provided"/>

An enum for xdg_surface-related errors…

<enum name="error">
	<entry name="not_constructed" value="1"/>
	<entry name="already_constructed" value="2"/>
	<entry name="unconfigured_buffer" value="3"/>

Wait, this one looks a bit different. Why isn’t there any summary? Why is the enum 1-indexed?

To be fair, that’s barely an issue. Most people reading the protocol will probably think to themselves: “Huh, must be a typo. And someone forgot to add summaries, not a big deal.”. And they might be right.

Except they shouldn’t think about it at all.

Imagine yourself hacking on a program¹ you like a lot and use every day; it just lacks That One Feature you need. You skim through the codebase to get the general idea of how is everything organized, find a relevant file and open it.

 * Enable the lights if they are disabled, disable them otherwise.
 * Returns 0 on success, 1 on failure.
int foo_toggle_lights();

/** Open the door.
 * NOTE: Using this function to open windows is deprecated.
void foo_open_door(foo_door_t *door);

 * Invites everyone to the party.
void foo_invite_everyone(foo_party_t *party, const char *invitation_msg);

Great!” you think to yourself, “I’ll add my function right here!

int foo_bake_pie(foo_pie_type_t type);

Remembering you want to be a disciplined programmer, you immediately start writing a documentation comment describing what the newly created function does.

…How do you write it, though? Should it say “Bake a pie” or “Bakes a pie”? Should the sentence be on the first or the second line of the comment?

Does it matter?” I hear someone ask. “Just copy whatever the comment above looks like and call it a day!” And indeed, that sounds like a viable strategy.

Except you shouldn’t think about it at all.

To the majority of readers those examples might seem not convincing, or even exaggerated. It doesn’t take much brain power to handle stuff like this and act properly, after all.

However, inconsistency is still distracting. It breaks the flow and increases perceived complexity, so it becomes harder to find repeating patterns and get a full understanding of the system quickly. There is a reason why people write and follow code style guides, use formatters, create design guidelines: they try to save you from making unimportant choices by providing ready answers, letting you focus on what actually matters.

Thinking is already hard as it is; don’t force me to do it for more than necessary.

¹ While this example is based on a hypothetical project, I’ve seen this happening in real-world programs and libraries maybe just one too many times.