Mastering Sustainable Software Evolution
As in biology, changes in the environment fuel evolution. For business software, the environment is the marketplace. As Figure 1 illustrates, and we have explored before, we can readily identify five types of market forces: innovation, cost reduction, growth, regulation, and coevolution.
While these environmental factors fuel software evolution directly, they also have a significant indirect effect. Each change inevitably introduces bugs. As a result, the initial change indirectly leads to the need for further changes (bug fixes) down the line.
These (direct and indirect) evolutionary pressures from the marketplace explain Lehman’s first law of software evolution (continuing change). But, according to laws two (increasing complexity) and five (conservation of familiarity), such evolution rapidly runs into problems if the size and complexity of the system are allowed to increase unchecked.
Sustainable evolution requires two more types of change, not fueled by the marketplace, that must be initiated internally (refer back to Figure 1):
Refactoring. The complexity of the program code of a software system can be reduced through a series of small changes that improve its structure but preserve its behavior. Performing such incremental code improvements is called “refactoring.” While refactoring takes effort, it does not provide any direct value in terms of new functionality. For this reason, developers may find it difficult to justify their refactoring efforts to their colleagues and managers. Nonetheless, timely and judicious refactoring is generally considered a best practice to keep code complexity in check and, hence, prevent the second law of software evolution (increasing complexity) from kicking in.
Commoditization. Modern software is not built in a vacuum. Rather a range of generally available functionality from external software libraries, frameworks, and services is used as a platform on top of which new, innovative functionality is created. However, the boundary between “innovative” and “generally available” shifts over time. Pieces of functionality that you developed inhouse several years or perhaps just a few months ago may have given you a competitive edge then. But others have taken notice and developed similar components, perhaps in a more cost-effective, smarter way. And recognizing the general utility of this functionality, they may have made it available as a reusable component. Your once-unique functionality has become a commodity, and the smart thing to do is to exchange one for the other. Doing so will relieve your team from continuing to evolve that functionality, and you can instead focus on functionality that makes you unique and competitive. While swapping homebrew against commodity may involve substantial effort, it can prevent the fifth law of software evolution (conservation of familiarity) from killing the productivity of your developers.
Since refactoring and commoditization are not sparked by external pressures, they are easily forgotten or de-prioritized in favor of other types of change. They are motivated by long-term sustainability and require a degree of foresight that may feel antithetic to our fast-paced digital age. The judicious application of refactoring and commoditization is a critical success factor, nonetheless.
Mastering Software Evolution
So what are the essential capabilities for any organization to master software evolution, both on the engineering and on the leadership level?
Mastering software evolution means that leadership, the business, and engineers need to work together to balance the various evolutionary pressure factors in such a way as to sustainably outpace the competition. Sustainability means bringing new functionality to the market while keeping your underlying software assets healthy and nimble.
To achieve such balance is easier said than done. It requires a form of data-driven design and decision making that balances commercial and technical considerations. Let’s review what is needed in terms of data, design, and decisions.
To feed your design and decision-making processes, your organization needs to look for data in at least three directions:
Look around. At any moment, your organization needs to know what is available in terms of reusable libraries, frameworks, services, tools, and technologies. And you need to have an informed opinion on which of those may be useful to you. To get this information, you can tap into external sources, such as the ThoughtWorks Technology Radar. For a selected shortlist, you should consider a more experiential approach such as a hackathon.
Look inside. Your organization needs to have an up-to-date inventory of its software assets. This is not just a list of systems, but also their interdependencies and their properties, including volume, quality, rate of change, functional focus, and technical health. To get this information, your software development infrastructure needs to include measurement instruments, such as code scanners, architecture analysis tools, and metric dashboards.
Look ahead. Your organization needs to know which market needs you will likely need to satisfy in six to 12 months. To get this information, you need to have product owners on board that curate a high-level backlog of product ideas that can be detailed out into feature descriptions. To collect these product ideas, your product owners need to interact intensively with users, business representatives, thought leaders, and (whenever possible) competitors.
These three data streams provide the basis for design and decision making regarding software evolution.
Your organization’s software design process must go beyond the new functionalities you want to add. Design must also deal with restructuring and eliminating existing software components:
Refactor. The purpose of refactoring is to improve the design of the system to counteract complexity increases (as described above), as well as to prepare for upcoming additions and deletions. Your software teams must master the art of refactoring and must have the mandate to invest the necessary effort.
Enhance. An optimally designed enhancement not only takes into account what should be added in terms of functionality, but also how and where in the software the changes are to be implemented, given what is known about the current state of software components, their interdependencies, and their likely commoditization trajectory. (For example, if your next feature gives you a unique competitive advantage, its implementation should not be mixed in with low-level generic functionality; it should be designed to remain confined to higher-level, product-specific components.) Your product owners, architects, and developers must optimize enhancement designs together.
Eliminate. The design activity should include determining what functionality to remove (or replace by commodity components), when, and how. Functionality that is no longer desired may have been inextricably woven into the code, such that significant refactoring is needed before it can be replaced. Good software design takes future replaceability of functionality into account.
Only a small part of software development is about editing code, while a very large part is about fast, effective, and coherent decision making at all levels of the organization. The sheer number of decisions to be taken implies that not all decisions can be made at the top. A large degree of decentralization is needed, while coherence across the organization is safeguarded by shared goals that are set centrally:
At the engineering level, teams and individuals are empowered to make fast, informed decisions about as much as possible. These decisions are constrained by shared goals.
At the leadership level, constraints for decentralized decision making are set in terms of goals (what and when, not how). These goals must be continuously reviewed and any changes in these goals must be communicated clearly and broadly within the organization.
So what are the critical questions you should ask if you want to determine whether your software evolution capability is ready for what lies ahead? After transformation comes evolution. Is your organization ready for the long haul? Here are some critical questions to ask yourself:
Does everybody know and agree on which of our software components give us competitive advantage? Are the teams assigned to these components aware of what new functionalities likely need to be added in the next six months? Are required changes to these components made diligently and without compromising their structural quality?
How about the components that do not give us competitive advantage? Are they few and well isolated? Do we have a clear plan and timeline for removing them or replacing them with commodity components? Is that timeline shorter than three months?
Do our engineers enjoy and master the art of continuous removal of the nonessential elements of the portfolio? Are we refactoring the codebase continuously, and in small increments, to prepare for removal of noncore components? Are we removing code at the same pace as adding code? Do we celebrate decommissioning components even when we (recently) invested blood, sweat, and tears to create them?
If your answers are affirmative, you are able to keep your software healthy, nimble, and focused on what you need to compete. If not, you will gradually lose the ability to adapt quickly to new market needs, regulations, and technological opportunities. The world moves on while your software falls behind.
[For more on this topic from the author, see “The World Is Eating Your Software.”]