Monday, July 27, 2020

The golden rule of software quality


This post summarizes a rule of thumb that I commonly cite in software quality discussions, so that I can link to my own post to save time. I have taken to calling this the “golden rule of software quality” because the rule is succinct and generalizable.

The golden rule is:

Prefer to push fixes upstream instead of working around problems downstream

… and I’ll explain implications of this rule for a few software engineering tradeoffs (using examples from the Haskell community and ecosystem).

Disclaimer: The golden rule of software quality bears no relationship to the golden rule of treating others as you want to be treated.

Third-party dependencies

Most developers rely on third-party dependencies or tools for their projects, but the same developers rarely give thought to fixing or improving that same third-party code. Instead, they tend to succumb to the bystander effect, meaning that the more widely used a project, the more a person assumes that some other developer will take care of any problems for them. Consequently, these same developers tend to work around problems in widely used tools.

For example, for the longest time Haskell did not support a “dot” syntax for accessing record fields, something that the community worked around downstream through a variety of packages (including lens) to simulate an approximation of dot syntax within the language. This approach had some upsides (accessors were first class), but several downsides such as poor type inference, poor error messages, and lack of editor support for field completions. Only recently did Neil Mitchell and Shayne Fletcher upstream this feature directly into the language via the RecordDotSyntax proposal, solving the root of the problem.

The golden rule of software quality implies that you should prefer to directly improve the tools and packages that you depend on (“push fixes upstream”) instead of hacking around the problem locally (“working around problems downstream”). These sorts of upstream improvements can be made directly to:

  • Your editor / IDE
  • Your command-line shell
  • Programming languages you use
  • Packages that you depend on

Note that this is not always possible (especially if upstream is hostile to outside contributions), but don’t give up before at least trying to do so.

Typed APIs

Function types can also follow this same precept. For example, there are two ways that one can assign a “safe” (total) type to the head function for obtaining the first value in a list.

The first approach pushes error handling downstream:

-- Return the first value wrapped in a `Just` if present, `Nothing` otherwise
head :: [a] -> Maybe a

… and the second approach pushes the requirements upstream:

-- Return the first value of a list, which never fails if the list is `NonEmpty`
head :: NonEmpty a -> a

The golden rule states that you should prefer the latter type signature for head (that requires a NonEmpty input) since this type pushes the fix upstream by not allowing the user to supply an empty list in the first place. More generally, if you take this rule to its logical conclusion you end up making illegal states unrepresentable.

Contrast this with the former type signature for head that works around a potentially empty list by returning a Maybe. This type promotes catching errors later in the process, which reduces quality since we don’t fail as quickly as we should. You can improve quality by failing fast at the true upstream root of the problem instead of debugging indirect downstream symptoms of the problem.

Social divisions

I’m a firm believer in Conway’s Law, which says:

Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.

— Melvin E. Conway

… which I sometimes paraphrase as “social divisions lead to technical divisions”.

If social issues are upstream of technical issues, the golden rule implies that we should prefer fixing root causes (social friction) instead of attempting to mask social disagreements with technical solutions.

The classic example of this within the Haskell community is the cabal vs. stack divide, which originated out of divisions between FPComplete and Cabal contributors (Corrected based on feedback from the Haskell subreddit). The failure to resolve the upstream friction between the paid and open source contributors led to an attempt to work around the problem downstream with a technical solution by creating a parallel install tool. This in turn fragmented the Haskell community, leading to a poor and confusing experience for first-time users.

That’s not to imply that the divide in the community could have been resolved (maybe the differences between paid contributors and open source volunteers were irreconcilable), but the example still illustrates the marked impact on quality of failing to fix issues at the source.


Carefully note that the golden rule of software quality does not mandate that you have to fix problems upstream. The rule advises that you should prefer to upstream fixes, all other things equal. Sometimes other considerations can prevent one from doing so (such as limitations on time or money). However, when quality is paramount then you should strive to observe the rule!


  1. Well written, thank you...

  2. Hi Gabriel,

    Thanks for the article - very thought-provoking as usual :)

    There's also a case for mixing the options when it comes to third-party dependencies. In the Scala2 eco system, a lots the languages problems are solved by third party libraries such as Shapeless, Enumeratum and Simulacrum and KindProjector to name a few. The upside is that now the Scala community can see what is needed and what actually works and then roll this functionality back into the Scala compiler. This has been done in Scala 3. This is not the norm, but it can be used to experiment cheaply to see what is possible, usable and where it breaks down.

  3. Hi Gabriel,
    Thank you for sharing this! I'm one of the writers working for InfoQ China. I wonder if you would willing to let us translate this article to Chinese and repo on our website? Of course, we will cite you and your original post, and it would not be used for commercial purposes, only to provide info. Thanks again.

    1. Yes, you can freely translate this and other articles to Chinese. All of my written work is licensed under a Creative Commons Attribution 4.0 International License

  4. This is so true, but I'm afraid the target audience defies to listen. Interested and pushing only their own agendas, disdain cooperation and mutual communication. Compare that situation with flourishing community around Rust, for example…