Organizations often attempt to define software engineering tenets (aka core values or principles) that serve as a static mental and behavioral model to guild employees. Tenets are a powerful tool capable of inspiring people toward a common goal. However, poorly designed tenets can just as easily unite a community on a myopic quest toward inefficiency.
Tenets are essentially a simplified model of a problem space that reduces the cognitive load associated with decision making. The intent is to provide a shorter path to a good decision. These models often become a deeply ingrained component of engineers' psyches which are subject to the human proclivity of self-defense. Unfortunately, models that are overly-simplistic or inaccurate provide a shorter path to a bad decision that may be defended with disregard toward reason. This is why it's important to avoid tenets that promote silver bullet thinking or preclude reasoning from the engineering process.
Humans’ Affinity Toward Their World View
Humans have a deeply ingrained psychological need to categorize and simplify the world around them. Notice the deliberate use of the word need; it's much more than a mere idiosyncratic proclivity. Reducing complex phenomena to comprehensible models that can be conjured up by a single word or phrase makes the world tractable. Conflating new experiences with known phenomena provides the ability to quickly predict the behavior of the environment and the objects therein. The inevitable consequence is that models become part of the psyche: a deeply ingrained component of one’s world view. The whole of cognition rests upon this ability. This is the thesis of Douglas Hofstadter's seminal paper Analogy as the Core of Cognition. It would be easy to make a case that the many of humanity’s prodigious accomplishments are a consequence of this need.
Unfortunately, the same case could be made for many of humanity’s most heinous atrocities. Often, the simplified models humans create to predict the behavior of their environment are incomplete or inaccurate. These models are often expanded well beyond what is appropriate (see the concept of illusory correlation as defined by Stanvoich). To make matters worse, humans pugnaciously defend their world view as a self-defense mechanism (refer to Jonas Kaplan's body of work for a more comprehensive treatment of the theory). Self-defense operates at a baser level with indifference toward reason. This is essentially what Max Planck was referring to when he said, "An important scientific innovation rarely makes its way by gradually winning over and converting its opponents: What does happen is that the opponents gradually die out”. The Oatmeal illustrates this concept beautifully with his "You're not going to believe what I'm about to tell you" comic (https://theoatmeal.com/comics/believe).
Human's ability to overcome this self-defense mechanism is a debate that reaches back to the 1700s with Kant's subject-object distinction. The argument comprises the very essence of the modern/post-modern philosophical dichotomy. The conclusions that have evolved out of these two lines of thoughts are truly astounding. Drawing any sort of conclusions about objective reality is well beyond the scope of this humble article. That is best left to the purview of philosophers. However, for the purpose at hand, there are a few salient points that can be accepted as fact:
- Humans need models to make the world tractable
- Models can be incomplete or inaccurate
- Models become part of human's world view
- Humans default to a bellicose attitude when protecting their world view
To relate these concepts to defining software design tenets, consider the interactions between software professionals and projects as a microcosm of the interactions that humans have with the world.
The Software Engineering Microcosm
Modern software professionals find themselves submerged in ambiguity. An epiphenomenon of the industry's relative adolescence is that the marketplace of ideas hasn't had enough time to standardize on best practices. There are seemingly countless competing concepts battling it out as you read this. Unless you can predict the future, there is no way to tell which ones will emerge victorious. Furthermore, the industry is severely lacking in meaningful empirical studies so it's hard to even define what "victory" means. Coupling all of this with ever-shrinking timelines, ever-evolving external security threats, ever-expanding feature requirements, and relentless pressure to generate revenue and things quickly appear unmanageable to the ablest of people. The plight is analogous to the struggle of an underprivileged juvenile trying to build meaningful models of the world without proper guidance. Surely not an impossible task, but success (depending on how you define it) is statistically improbable.
Make no mistake about it, the software industry needs simplified models in order to impose order on the chaos. The problem is, the industry has evolved a particularly pernicious tendency toward overly simplistic models. Typically, the models look something like this: Company X is an industry leader, they use Technology/Pattern Y, therefore Pattern Y is the solution to software productivity. Unfortunately, software professionals are still questing for the proverbial "silver bullet" which Fred Brooks denies the existence of at all.
Examples of fashionable mental models following the pattern above are virtually endless. Those who were programming in the early nineties (as was this author) undoubtedly remember the promised panacea of CASE tools. Next came n-tier architecture with its unfettered flexibility. Many espoused Ruby and other dynamically typed languages as the one true way because they lowered the barriers to entry. Conversely, proponents of languages such as Haskell proselytized expressive types systems as the end of the software crisis. There is undoubtedly much missing from the list. The latest silver bullet fervor is channeled toward micro-services and twelve-factor apps.
Without a doubt, there is someone reading this who is feeling dissonance because their world view has just been challenged. The author sympathizes with said reader because he has been in his/her shoes. As a young engineer, the author was challenged by a senior engineer on the efficacy of n-tier architecture. The immediate response was, "You are simply afraid of change and new paradigms. This is the RIGHT way to build software". It's amazing how time and experience changes one's perspective.
To summarize the key points:
- There is much ambiguity around best practices in modern software engineering
- Software professionals often adopt mental models that equate to silver bullet thinking; and this is irresponsible
- When mental models become part of a software professional's world view they become defensive of them; and this reflects personal bias which must be acknowledged and questioned if one is to every grow because nothing is static
Don't make the mistake of interpreting this piece as an attack on any particular architectural pattern. Every model mentioned has undeniable merit. The underlying fallacy stems from silver bullet thinking that espouses the RIGHT (and suggestively ONLY) way to build software.
The RIGHT Way to Build Software
A reasonable person could challenge the thesis of this article by enumerating the benefits of their said model and asserting that it may not be a silver bullet, but it is certainly a superior approach - so it is, therefore, the right way to build software. The glaring fallacy in that reasoning is that it attaches value propositions outside of a context. This is what Micheal Glinter would classify as Level One Thinking.
Robert Glass has written extensively on this topic in several books and articles. In his book Software Creativity 2.0 he states the following concerning silver bullet thinking: "we have those who proclaim each new idea that comes along as the solution to software productivity. ... These people, I would assert, are the level one thinkers. Some perceive them as strong because they see a solution clearly and move swiftly toward it. Others see them as simplistic, for they ignore the complexity in the problem and seem unable to accept the ambiguity."
Unfortunately, the second law of thermodynamics applies to software: there is no such thing as a free lunch. Architectural patterns are nothing more than a series of trade-offs. Two pertinent points follow from this assertion. The first is that any person who only enumerates advantages without also explaining the cost of said benefits, simply DOES NOT have a deep understanding of the architectural pattern or is selling something. The second is that it is nonsensical to weigh trade-offs outside of a context. Each project has unique requirements that give meaning to trade-offs. No trade-off is always best, but it may be best for the use case in question.
Evaluating trade-offs outside of a context is not only nonsensical, it's also destructive and the pernicious effects are virtually never-ending. It diverts the focus from the software's true purpose of providing value to end users to arbitrary patterns. Concisely stated, it prioritizes the process above the product. This equates to wasted efforts and resources. Furthermore, once engineers' adopt an overly-simplified mental model, they are psychologically incentivized to pursue and perpetuate it.
- There is no “right” way to build software
- Every approach is a series of contextual trade-offs
- It's nonsensical to evaluate trade-offs outside of a context
- Evaluating trade-offs outside a context prioritizes the process over the product
In the event that an organization must communicate an affinity toward a trade-off, these should be considered non-functional requirements and not tenets.
Non-Functional Requirements are NOT Tenets
It makes sense that some organization would want to communicate an affinity toward a side of a trade-off. For instance, financial institutions may naturally prioritize security over availability. Specifying these preferences is particularly important because they typically never come to bear from iterative development. However, it makes more sense to express these as non-functional requirements instead of tenets. This may seem like nothing more than linguistic gymnastics; however, the psychological effect is very real. Consider the meaning of the two words:
Tenet: a principle, belief, or doctrine generally held to be true (Merriam-Webster)
Non-Functional Requirement: a requirement that specifies criteria that can be used to judge the operation of a system, rather than specific behaviors (Wikipedia)
Specifying a trade-off as a tenet removes reason from the engineering process and has the unintended consequence of creating an overly-simplistic model of the software engineering world. Conversely, specifying a non-functional requirement biases the engineer toward an engineering mindset.
There is one key takeaway here:
- Specifying a non-functional requirement as a tenet has the unintended psychological effect of removing reason from the engineering process
Hopefully, this section made the case that it's undesirable to specify non-functional requirements as tenets and the previous section established that silver bullet thinking is destructive. The question remains, what exactly is a good software engineering tenet?
Anatomy of Healthy Tenets
A healthy tenet is ambiguous enough to facilitate the engineering process yet precise enough to focus effort. One of the best examples of a healthy tenet is Amazon's Customer Obsession principal:
Leaders start with the customer and work backwards. They work vigorously to earn and keep customer trust. Although leaders pay attention to competitors, they obsess over customers.
The principal does not bias engineers toward any particular technology or pattern. Likewise, it doesn't create a cognitive bias toward an approach. However, it does bias thinking toward the deep compilation of use cases. In short, it keeps the focus on the product, not the process.
Another perfect example of a good tenet is Amazon's Invent and Simplify principal:
Leaders expect and require innovation and invention from their teams and always find ways to simplify. They are externally aware, look for new ideas from everywhere, and are not limited by “not invented here”. Because we do new things, we accept that we may be misunderstood for long periods of time.
The attribute that makes this tenet great is that it encourages the relentless pursuit of the perfect balance of trade-offs. It creates a bias toward simplicity. It does not remove reasoning from the process; rather it forces proponents to think critically. These are the kind of tenets that create harmony.
As a small aside, make sure to avoid the trap of copying the tenets of industry leaders. The big technology companies didn't get where they are by forcing solutions into their contexts. They aggressively engineered solutions to meet their contextual needs. It only makes sense to be aware of the practices of successful enterprises; however, accept that they operate in a different context from yours. Create solutions for YOUR context.
To summarize, good tenets:
- Do not blindly prescribe any particular technology, approach, or pattern
- Require proponents to think critically at all times, this includes questioning self to guard against one’s own bias
- Create a bias toward simplicity but not over-simplicity
- Focus on products, not process
Wrapping it Up
Human cognition is a fascinating subject with profound implications for the engineering process. Regardless of an individual's reasoning ability, they are limited by their cognitive architecture and subject to the perils of defending their contextual world view. Accepting software engineering tenets contributes to an engineer's world view. This is why it is important to create tenets without unintended negative biases (to the extent that's possible) and to revisit and revalidate them over time.
Tenets that perpetuate "silver bullet" thinking or establish non-functional requirements are particularly noxious. They tend to exclude critical thinking from the engineering process. Additionally, they place emphasis on the process rather than the product. Conversely, healthy tenets refrain from prescribing any particular technology, approach, or pattern. They force proponents to think critically and choose the perfect balance of trade-offs. With a bias toward simplicity, the focus remains firmly on the product.