Application Agility: Overcoming the Complexity Barrier
Complexity stands in the way of application agility. At its core, a complex system creates a fear of change. Once that fear takes hold, organizations and engineering teams mitigate it by adding layers of processes to ensure quality. This results in a development culture where code is only ever added, not changed or removed, to avoid the risk of unintended bugs. Over time, this leads to increasing complexity in both processes and systems architecture.
What Creates Application Agility?
To achieve true application agility, we need a combination of automated tests, automated builds, clear APIs and contracts, robust DevOps pipelines, and responsible use of technical debt. Automated tests and builds are fundamental; they provide the safety net that allows developers to make changes with confidence. Clear APIs and contracts minimize dependencies between systems, enabling teams to work independently and efficiently. Robust DevOps pipelines ensure that the entire development process is streamlined and reliable. Lastly, technical debt should be managed responsibly, with a clear plan to pay it down over time.
Despite knowing what is needed for application agility, many organizations struggle to establish and maintain it. The main culprit? Legacy applications. These systems, core to driving business value, are where the majority of IT budgets are focused. They also hold the potential for the biggest performance gains and the largest cost savings. However, the tools and techniques that keep software soft and agile work best on new applications, where the code is still simple and easy to change. In contrast, legacy systems have often been complicated by suboptimal practices and organizational pressures over time.
Restoring Agility to Legacy Applications
To restore health to legacy systems, we need a structured approach. First, we must understand our product structure. Software exists to bring value to the customer by supporting a product. Therefore, it needs to be easiest to change within the product’s boundaries. This requires reworking the system architecture to align with the product architecture. The goal is to increase independence and minimize dependencies so that a product enhancement can be built, tested, and deployed with minimal coordination with other products. Where systems must interact, clear APIs, contract tests, mocks, and fakes allow teams to govern interactions without the need for manual coordination or testing.
The next step is to determine the level of refactoring required. This depends on the complexity of the current system and the degree of misalignment between the system’s agility and the product’s demands. Core functions that change infrequently may only need to be stabilized and encapsulated so that other functions can easily integrate and leverage the core. More strategic functions might need to be extracted and componentized so changes can be more easily and reliably made. Where rapid innovation is required, the investment may be warranted to rewrite or rebuild the code to create maximum agility. Refactoring legacy code is often a balance between immediate business needs and long-term technical improvements.
Prioritizing the backlog of remediation work based on business value is crucial. Not all code needs immediate attention and what does require attention can’t all be done at once. By focusing on areas that offer the greatest return on investment, we can make meaningful progress without overwhelming the engineering teams.
Finally, uplifting software engineering practices is essential to maintain and enhance simplicity. This involves adopting modern development practices such as test-driven development, continuous integration, and continuous deployment. By embedding these practices into the development process, we can reverse the cycle of increasing complexity and make the software easier to maintain over time.
Practical Example: E-commerce Transformation
Consider the case of a large e-commerce client. Initially, this client struggled with transitioning from dealership sales to e-commerce due to the complexities of their legacy systems. They quickly ramped up their e-commerce capability but found that adding more teams and vendors slowed them down, rather than speeding them up. Quality suffered, and lead times increased.
To address this, we focused on reducing dependencies and simplifying their system architecture. Here's how we did it:
As a result of these changes, we saw a significant improvement in software quality and delivery speed. Automated tests led to a 70% decrease in defects, and the reduction in dependencies resulted in a ninefold increase in cycle time efficiency. The teams could now work more independently and efficiently, leading to faster deployment of new features and enhancements.
Conclusion
In conclusion, restoring application agility to legacy systems is both challenging and essential. By understanding our product structure, reworking system architecture, determining necessary refactoring, prioritizing remediation based on business value, and uplifting engineering practices, we can reverse the cycle of increasing complexity. This approach not only enhances the quality and reliability of our software but also supports long-term agility and growth. Successful application modernization is about making informed decisions that connect technical changes to business value, ultimately making our software easier to maintain and adapt over time.
Originally Published: https://go.leadingagile.com/kbg