In Defense of Ignoring All Rules

March 10, 2020

On Wikipedia, one of the five most prominent guidelines is to ignore all rules. At time of writing, the website amalgamate's consensus has settled on the phrasing as follows:

[…] sometimes improving Wikipedia requires making exceptions. Be bold, but not reckless, in updating articles.

In the link above we see the text of Wikipedia: Ignore all rules or WP:IAR:

If a rule prevents you from improving or maintaining Wikipedia, ignore it.

While intentionally vague, the idea that principles apply in most-but-not-all situations is a core tenet of why newcomers to Wikipedia can have a decent time and see their changes preserved. Chances are that frequent readers of Wikipedia are going to understand the tone to strike, the passive voice, and other implicit essentials to making a good edit. So why create a set of rules that can't be violated? Of course, going too far into the weeds can result in ideological differences that can't be reconciled with the hivemind, but more basic changes of the sort that newcomers would make are unlikely to bring about such opposition.

In my view, this translates quite well into the modern view of software development in teams: for consistency, matters of design (systems design, API design, etc.) require specific standards set within the group (consensus) or above it (directive), but the development (implementation details, coding style, etc.) tend to be less heavily policed. This is good; it is important to keep developers on-task building the right stuff, but bad to nitpick matters of opinion unless they are consequential to the final product.1

I have worked on solo projects where I adhered to certain principles, and been a member of teams where I deemed it inappropriate to do so. Consider the following statement I could make as to why:

When I determine whether to adhere to a principle,2 I naturally do so in the course of attempting to make the most efficient choices for the project at hand.

Surely an engineer with different priors could responsibly make the opposite choices as me and utter the same sentence, yes? Well, probably, but let's go through some examples with increasing amounts of context.

Zen and the Art of Maintaining Someone Else's Code

In the beginning, Engineer Arthur created an Application. This had never been done before, and no one else knew how to do it, so Arthur built an appliication that made sense to him. When someone asks him how something works, he can coherently explain it, and then the other person understands. Great! When something goes wrong, Arthur can find the bug and fix it. Now the program works again. Also great!

Arthur makes a lot of money from this Application and retires. Now his son, Bob, is put in charge of the Application. Bob thinks differently than Arthur and doesn't have the intuition his father did. When someone asks him how something works, he has trouble explaining it. He has to study the codebase. This is fine, but the codebase doesn't follow any standards—it's tailored to how one specific person thinks—so it takes forever to interpret. When something goes wrong, Bob doesn't have a strong enough mental map to find it quickly. In this way Bob is frustrated by his project but can ultimately deal with it. Bob spends his career like this and retires, leaving his twins, Charlie and Daniel, to mantain the Application in his stead. Before Bob retires, though, he writes some rudimentary documentation so the problems he faced won't repeat themselves.

Charlie and Daniel kick off their understanding of the Application by reading Bob's documentation. It's pretty good, and the twins can maintain the project reasonably well with it. They gain an understanding faster and feel more confident, so they even have time to implement some new features! Great! Charlie and Daniel independently decide to build integrations with Service 1 and Service 2, which are pretty similar. They work on their solutions and add them to the application. They both work, they don't introduce any new bugs, and everyone is happy. Great! Eventually, Charlie and Daniel move on with their careers and turn over the reins to Eric.

A while later, newly-minted principal engineer Eric needs to update the connections to Services 1 and 2. He reads the code for each—it's terrible! Redundant! The two are completely different, but they could've shared so many common functions! Not without some frustration, Eric determines that Service 2's integration is much better and completely rewrites Service 1's integration to be analogous to it. Then Eric updates the documentation with some guidelines for setting up an eventual Service 3. In this way Eric has created some guidelines for a standard implementation of a new service. Satisfied, Eric is comfortable moving on to a new project, so he steps down and lets Finn take over for him.

Finn hires an associate engineer, Grant. Years later, the time has come to implement Service 3. Services 1 and 2 were so complex; comparatively, Service 3 is very simple, so Grant can heavily simplify the implementation. The finished product is simple, elegant and easy to follow. Grant eagerly submits the code to his manager Finn for review. Finn takes an hour to read Grant's work, and then sends it back. Why? Grant didn't follow the guidelines! Finn suggests a number of changes to the Application. These increase its complexity substantially, but now it follows the guidelines—and it's more analogous to the other Service implementations.

This frustrates them both:

Who's right? Vote now on your phones.

Discussion

Notice a few things about this story. First, after five generations, Grant is making a decision much like Arthur, but on a different scale: Arthur made an entire application without standards; Grant is performing a smaller, more replicable task without standards. Arthur was an innovator, but iteratively his mindset would fall flat in a modern software development process. Grant is attempting to be selective in how he deviates from standards—this keeps him in line with WP:IAR.

Second, both Finn and Grant are valid in their frustration. In the real world, this would probably play out with Grant attempting to convince Finn that his decision to deviate from principle was correct, and the chips would then fall where they may. This is fine, as long as both avenues forward are considered. It may be that Grant is incorrect in his view that future codebase maintenance would be easier with a simpler implementation versus a more standardized one. It's a difficult call, and the conflict between simplicity and standardization tends to crop up a lot in API design: some endpoints are so simple that building an entire infrastructure around them seems pointless, i.e. “this one just fetches an exact object from a database, so why does it need all this bloat?“ But others in the same application are performing more intricate operations and need that bloat to stay coherent.

The “bloat” here typically relates to layers of abstraction: a simple API endpoint tied to a specific service may be able to expect a certain type of return object, manipulate it somehow and then return it. A more complex one might want to introduce some translation, one or more DTOs, etc. so that one day, if the tied service changes, it is easy to understand the exact place in the code that needs to change—everything else is standardized. More complexity + more external services = more abstract layering. This is a problem of interface segregation.

So how do you handle a project with mixed levels of complexity but a desire for standardized design? This question has come about several times in my career. What's my answer? Put simply, I believe that Grant is correct, i.e. ignoring the principles is the right idea 90% of the time. Noting that only half of these reasons relate to actual code, I explain:

Thus I urge software teams to consider appropriate policies for violations of established principles. Give your devs the freedom to break the rules and you will find yourself building a better Wikipedia!


  1. In other words, it's fine to criticize an implementation if a variant runs in linear time versus exponential time, but if two competing methods have the same time and space complexity, and the decision makes no consequence to the final product, it may simply irritate the developer. Ideally this would come out of feedback from (or meta-discussions surrounding) code review. 

  2. Brief clarification: here, “adhere to a principle” should be read as “follow the guidelines associated with a principle,“ thus the converse “ignore the guidelines associated with a principle.“ 

  3. In this example I would expect a responsible implementation to either check or explicitly set a property like InStorePickupCapable anyway, but the LSP doesn't care about anything but class definitions and these would be in violation. Of course, the solution is not to explicitly set InStorePickupCapable = true in the superclass; this property should only be set in each final subclass, so these classes probably should be refactored to be sibling classes with an abstract parent, etc. 

  4. Assuming, of course, that you can't do what the prior footnote suggests and refactor the classes altogether. Suppose it would be a highly laborious change to do the suggested refactor and make these classes siblings. In that case, it seems reasonable to at least consider carrying out this LSP violation. 

Licensed under CC-0 and of the public domain. Hosted with Cloudflare.
Created with ssg5 by Roman Zolotarev with a slightly modified modification based on one by Wolfgang.