In “Good Strategy / Bad Strategy”, Richard Rumelt provides a framework for strategic thinking. I found that the ideas from his book also translate to the design of software systems: The key is to have a good sense of the relative strengths and weaknesses of your current position and to apply coherent design decisions.
Rumelt proposes that every strategy development exercise should begin with the question “what is really going on here?”. Similarly, having a good system design hinges on getting our hands on the most important pieces of information. Of course, just like in other strategic situations there are usually many unknowns that we need to consider as part of the thinking process.
As engineers, when analyzing a situation, we sometimes content ourselves with receiving product requirements from a product management organization. I think this is insufficient. Even if product management is doing its strategic analysis correctly and engineering is doing the resulting system design diligently, each of the two parties operates with incomplete information, looking at different sides of the same coin.
As building software systems is an exercise in predicting the future, engineers need the relevant data to make such projections, and that might mean spending some time in problem space.
Strengths and Weaknesses
What is the outcome of such an analysis? Rumelt highlights that a good strategy is about using one’s relative strengths against the opponent’s relative weaknesses. To develop a good strategy we need to be aware of our own strengths and weaknesses first!
A “strength” from a system design viewpoint is any simplifying assumption that we can make and any feature or non-functional requirement we do not need to worry about: Is an occasional downtime acceptable? Can we accept some latency between an event and a response? Is returning inconsistent or stale data acceptable? Will data remain small? Can we limit our attention to modern browsers?
A “weakness” from a system design viewpoint is any requirement that constrains our design space: Are real-time guarantees needed? Will we require to maintain a secondary index on the data? Do old browsers need to be supported? Do we need to be compatible with some legacy system or compliant with some industry standard?
Having these assumptions mapped out helps identify the point in design space that provides the appropriate trade-offs.
One of the core ideas of Rumelt’s framework is that, in a good strategy, actions need to be consistent with a guiding policy.
What does that mean? In a business strategy context it means the following: Imagine you identified that your relative strengths could make you successful with a customer base of self-employed financial advisors and that this market is big enough for your growth ambitions. Then it makes little sense to have marketing produce material that targets big accounting firms and ask engineering to build features that would be important for a stock exchange. Your actions should all pull in the same direction to exploit the relative advantage as much as possible.
The same can be said for the engineering of systems. Once we have established that our strength is that data does not directly need to be consistent and that our weakness is that we have very hard response-time requirements, this can be formulated as a guiding policy that all subsystems should adhere to. If you pay a performance penalty in one sub-system to guarantee data consistency and break this guarantee in a different part of the system, you are likely to get the worst of both worlds. And if your strength is that older browsers do not need to be supported, it would not be coherent to spend time in some parts of the UI to provide this support but not in others.
Coherent actions multiply their effects. If my weakness is a very strict uptime guarantee, then investing in monitoring and alerting not only helps keep that promise but also makes it easier to identify improvement candidates. Redundant systems, alerting, and automated deployments are all tools that are great in themselves but unfold their biggest potential when combined: When an alert is raised, a redundant system might be available for quick mitigation while a structural fix of the problem makes it into production quickly and safely thanks to the automated deployments.
Building software systems is, among other things, a strategic exercise. Formulating the trade-offs that we can make explicitly, allows us to take coherent technical decisions in every sub-component and make them work together coherently. Failure to do so results in efforts pulling in multiple directions without amplifying each other and in the worst case even canceling each other out.
Leave a Reply