We asked developers from a variety of organizations and industries a simple question: “How long will your application continue to function if you are not allowed to touch its code?” What we found was that the more modern the application, the faster it would cease to work. Legacy applications would be fine for years but cloud native applications would crash and burn within weeks.
Software development has undergone significant changes in the last decade. Software is no longer built completely in-house and instead, there is now what is known as the software supply chain. It includes everything that goes into or touches software as it is being produced: developer tools (IDEs, CI/CD), third-party software (dependencies), or more pervasive parts of the application stack such as language runtimes, monitoring, and testing frameworks.
With this move to the software supply chain, software development has become industrialized. We are building more and more custom software, but producing it more quickly by integrating higher-level building blocks such as OSS or vendor APIs. Modern applications are glue code and as much as 90% of software can be from third-party components. Sophistication and availability of different components in the global software supply chain are what enable this rapid growth of custom software.
A common misconception about APIs is that they are stable interfaces and once you go with APIs rather than custom internal protocols, the communication between components remains well defined and stable. In reality, vendors of APIs do not remain static. They are developing their offerings based on the use cases they want to support: adding, deprecating, and deleting APIs as they do so.
This explains the paradox we described earlier that modern cloud native applications require more maintenance as third-party dependencies change and evolve at their own pace, requiring developers to constantly restitch the new versions back together. Legacy applications were built from scratch in-house, so even if you accrue massive technical debt and are not able to add features to the apps, at least they continue to function.
This restitching time is not trivial. Software maintenance typically takes 20-30% of the developer’s time. These are tedious repetitive tasks, but they require expertise that only the most senior developers possess and act as a tremendous drag on innovation and software quality.
Technical Debt Is a Wrong Term for This Ongoing Upkeep of Software
Technical debt as a term originated with the introduction of agile development. It is all about prioritizing speedy application delivery over perfect code. Alongside technical debt came the practice of refactoring as a technique to manage the code. Refactoring restructures the code without changing its external behavior. It addresses the technical debt and makes the required internal changes to the code necessary to support the latest iteration of the application and the new capabilities.
A common approach to managing technical debt is to make it engineering’s responsibility as part of each scrum sprint (the period when the development team works to complete a set amount of work) and treat it as an implicit cost of the process of enhancing the software. It’s under the control of engineering — they introduced the technical debt so they should address it.
We are building more and more custom software, but producing it more quickly by integrating higher-level building blocks such as OSS or vendor APIs.
We continue to use the term technical debt to denote activities that are internal to engineering and produce no tangible business benefits, but the cause of the debt has fundamentally changed. You didn’t take on technical debt knowingly when you incorporated the newest version of a vendor library into an application. And the next thing you know, the vendor releases a new version and your software is now out of date. This is supply chain management activities masquerading as technical debt.
When software is assembled using components from the software supply chain, maintenance is different as it’s no longer under the control of the developers and is instead tied to changes to third-party software and APIs. Modern software maintenance feels like a constant race against the clock to update the components to prevent the whole thing from breaking, and with no control over the changes to these third-party components, you are essentially running the race with your shoes tied together.
Software Supply Chain Maintenance Is Highly Repeatable
An additional thing we noticed when speaking with developers is the repeatability of their problems. Everyone is upgrading a similar subset of third-party software and incorporating it into their code. Some of it, like upgrades to common frameworks or language runtimes, impact practically everyone. Same with common CVEs.
Unlike the technical debt of the past where everyone had a unique set of technical debt and had to understand and address it through individual refactoring, software maintenance as upgrades of common components is repeatable across different codebases. Obviously, not everything can be automated, but significant chunks of tedious migrations can.
Beyond Refactoring — Automated Code Maintenance
What we need is an open ecosystem for developing and distributing automation for these tedious code maintenance tasks: upgrades, CVE patching, internal API maintenance. It can only be solved by a community through composing more and more complex refactoring operations and validating them on more and more code.
We can start with the same proven building blocks that an IDE provides, but make them shareable and composable, and applicable via different workflows. With these building blocks, API vendors and OSS framework authors can develop and distribute automation for upgrading consumers of their libraries and APIs together with the new versions of their libraries. Security researchers can not only disclose a vulnerability but provide an automated remediation for everyone impacted.
OpenRewrite is an OSS, Apache-licensed ecosystem of refactoring recipes aiming to create such a community. It’s early days, but we validated the approach by building a number of high-value refactoring recipes for Java framework migrations.
We imagine a world where the global software supply chain becomes a distributed and eventually consistent mono repo: at a point when you incorporate a new version of a library into your application, your glue code is automatically updated.
Feature image via Pixabay.