It used to fascinate me why some applications broke down over time and other applications continued to deliver value for years and years. What makes some system architectures rot over time and others stay strong and useful? Keep reading while I discuss why this happens.
Why Do Some Architectures Fail and Others Thrive?
Software Architecture is a broad field that makes up different sub-disciplines such as Systems Architecture and Application Architecture. At its core, these different Architecture expertise deal with defining the interfaces and the interactions between different systems. This often means setting the policy, rules, and restrictions for how different systems, applications, services, or modules interact with one another. It’s an important role to play on a team and I believe Architecting systems for Simplicity is the key indicator for whether or not an application delivers value or collapses under its own weight.
What is Simplicity?
Simplicity and its relationship to Easiness is best explained in Rich Hickey’s seminal presentation Simple Made Easy . To summarize: Simplicity is the result of focusing on the problem space and produces one role, task, dimension, or concept to solve the problem.
A thing is Simple when it is not entangled with another thing and they can affect each other. For example, the Unix command
find is useful for searching a filesystem for files or directories that match certain criteria like name or owner. The command
find Documents -name *.jpg -mtime +30 will find all JPEGS in the Documents directory that are older than 30 days. To use
find I don’t need to consider any side effects it had on my system which helps make it simple. A simple thing doesn’t necessarily mean that I can only do one operation either. Consider that I can change the arguments to
find and search for other types of files.
As you can see,
find is a fantastic tool for searching for a variety of files. However, if someone is not comfortable with working in a command-line environment, it isn’t as exciting to use the
find command. That person would have to get over their anxiety about using a command-line, learn to navigate the filesystem, and read documentation on using
find . Because the
find command is “out of reach” (as Rich Hickey puts it), imagine that this person ignores
find and looks for an installable software application that fits their immediate need — to find JPEG files. It is easier for this person to install the application. Easiness is the result of focusing on what’s at hand or nearest to our understanding and produces a complicated, less reliable way to solve a problem.
Obviously, this is fine for a person using their personal computer. But what if this person who chose the installed application was a programmer who was trying to automate “find a list of all JPEGs older than 30 days on this Unix system and email the results”. If they didn’t know
find or other Unix commands and wanted what was “easy”, they are now down a bad path. They solve the problem, but if the requirements change, maybe find all PNGs instead of JPEGs, or it’s uncovered later that the installed application has a bug in it. Maintenance becomes a nightmare and the system begins to break down because the focus was on how to solve the problem easily and not how to solve it simply.
We do this to ourselves all the time. Why? Because it’s easy and natural to do.
How To Architect Simply
Can going with “easy” ever be good?
Yes, if the importance of fast start-up time trumps long-term maintenance costs.
However, maintenance costs are very hard to predict early on and almost always underestimated. This is how teams find themselves drowning in Technical Debt on important applications. If the problem is very short-lived, then by all means go with easy! But, in my estimations, this is often hard to predict accurately.
So, how can we architect Simple Systems? From my experiences building and architecting systems, I’ve learned a few thoughtful ways to shape good, simple systems.
Focus on Breaking Down the Problem
To avoid easy, but broken, architectures focus you and your team on the Problem Space first. Take the time to prioritize analyze, diagnose, and explore the Who, What, Where, and (most importantly!) Why.
Break the problem down while being mindful that you are trying to find a simplest solution. It is best to avoid jumping to solutions during these conversations. Most of the time, these “tip of the tongue” spontaneous solutions are exactly the easy solutions we need to avoid.
Think Deeply About A Solution
Because you have done your duty by breaking apart the problem and fully understanding it, you equip yourself with the tools you need to find a good solution. After dissecting a problem space, the next thing to do is Think Deeply. Talk to peers, other teams, consult the internet, blogs, twitter, and whatever else.
Do The Research!
Also, remember there will always be multiple valid, but unequal, solutions to solve a problem. Each solution will have its own set of pros and cons, but there is rarely the perfect solution. So, once you’ve found a solution, go for it! Don’t get bogged down in decision paralysis.
Observe, Revisit, and Update Assumptions
OK, you’ve chosen a solution, you’ve architected your systems for simplicity, and you’ve deployed your solutions. Now, this is where DevOps Principles really come into play and can help guide Continuous Improvements on your architected solution. Your solution is out in the wild now delivering value, but application systems change and evolve over time. How can we make sure that still have a good solution to our problem?
Periodically, ask yourself these simple things:
- Is this easy to understand?
- Is this system easy to change?
- Is this system easy to debug problems?
- Is this system flexible?
- Have the fact patterns changed for the original Problem Space?
- Does the Problem Space still map well to this solution?
Answers to these questions will help guide Continuous Improvements of your architecture and how your systems interact together.
When Simple is Too Hard, Abstract
I believe that all application problems can map to simple system architectures. However, in reality (and business), we can’t all be experts and have unlimited knowledge. This is when you deploy Abstractions to bridge the chasm between simple and easy. In other words, Abstractions can help make hard, simple systems seem easy.
For an example of this, consider the
find example again. How might an Architect make the simple, but hard, solution more attainable for someone who doesn’t use Unix tools? Well, let’s say they really can’t understand Unix and command-line interfaces. Then, a solution might be to Abstract
find usage into something like a light-weight Web Interface with an HTML form.
For most people, an HTML web form is easy to use so we can use it to map to our
find command. You can imagine a set of fields that map to arguments and then when the command runs, it outputs the results to the browser. This probably isn’t the “best” solution, but gives an idea of how someone might use Abstraction.
I hope that this post inspires you to consider Simplicity as a goal when designing and architecting systems. If your experiences match mine, then you’ll find that Easy too often wins out over Simplicity. We can change that!
Remember to stress understanding your problem space, thinking deeply on a solution, and abstract when necessary. If you take these things to heart and always continue to iterate over your system architectures, then you’ll begin to see transformative change on your team and in your product. Good Luck!