In today’s fast-evolving technological and marketplace environments, the software applications that companies depend on must be able to quickly adapt to the demands of an ever-changing competitive landscape. But as existing features are modified and new ones added, an app’s codebase inevitably becomes more complex, making it increasingly difficult to understand, maintain, and upgrade. How can developers ensure that their apps remain flexible and adaptable even as changes are incorporated over time? They can do so by applying the principle of continuous refactoring.
As Techopedia notes, the purpose of refactoring is to improve properties such as readability, complexity, maintainability, and extensibility. When these factors are regularly enhanced through continuous refactoring, apps can maintain their utility well into the future.
But, as management guru Peter Drucker famously said, “You can’t improve what you don’t measure.” That’s what architectural observability is all about: ensuring that developers have the ability to see and quantitatively measure the conditions they need to address through the critical process of continuous refactoring.
Why Modernization Requires Continuous Refactoring
Application modernization can be described as the process of restructuring old apps to eliminate technical debt and seamlessly integrate them into today’s cloud-centric technological ecosystem. Technical debt is typically a major stumbling block in such integrations because it, by definition, involves “sub-optimal design or implementation solutions that yield a benefit in the short term but make changes more costly or even impossible in the medium to long term.” In other words, apps afflicted with significant amounts of technical debt can be extremely difficult to upgrade to meet new technical or competitive requirements.
The modernization process typically begins by refactoring legacy apps from a monolithic architecture (in which the codebase is organized as a single unit that has functional implementations and dependencies interwoven throughout) to a cloud-native distributed microservices architecture. But application modernization doesn’t stop there.
Because the cloud environment is constantly evolving, with technological innovations being introduced practically every day, new technical debt, and the decrease in adaptability that comes with it, begins to accumulate in an app from the moment it’s migrated to the cloud. That’s why modernization can’t be a one-and-done deal—rather, it’s a never-ending, iterative process. Martin Lavigne, R&D Lead at Trend Micro, puts it this way:
“Continuous modernization is a critical best practice to proactively manage technical debt and track architectural drift.”
Since refactoring is fundamental to the modernization process, continuous modernization necessarily involves continuous refactoring to remove technical debt and ensure that apps maintain their adaptability and utility over time.
The Impact of Architectural Drift
As Lavigne’s statement indicates, tracking and addressing architectural drift is a critical element of a successful application modernization program. That’s because architectural drift is one of the main sources of the new technical debt that migrated apps constantly accumulate as they operate and are updated in the cloud. So, what is architectural drift and why is it so important for ongoing application modernization?
Apps are typically designed or modernized according to a coherent architectural plan. But as new requirements arise in the operating environment, quick changes are often made in an unregulated or ad hoc manner to meet immediate needs.
As a result, the codebase and architecture begins to evolve in directions that are not consistent with the original architectural design, and technical debt, in the form of anti-patterns such as dead code, spaghetti code, class entanglements, and hidden dependencies, may grow. To make matters worse, such changes are frequently documented sparsely—if at all.
This inevitable accumulation of architectural technical debt over time is what architectural drift is all about. And it can be deadly. In an article that describes architectural technical debt as “a silent killer for business” Jason Bloomberg, Managing Partner at Intellyx, makes this comprehensive declaration:
“One of the most critical risks facing organizations today is architectural technical debt. The best way to keep such debt from building up over time is to adopt Continuous Modernization as an essential best practice. By measuring and managing architectural technical debt, software engineering teams can catch architectural drift early and target modernization efforts more precisely and efficiently.”
But that’s not an easy task. Architectural drift is difficult to identify and even harder to fix. The problem is that application architects have traditionally lacked the observability required for understanding, tracking, measuring, and managing architectural technical debt as it develops and grows over time. And, to paraphrase Peter Drucker’s maxim, you can’t improve what you can’t observe.
The Basics of Observability
Observability is an engineering term of art that refers to the ability to understand the internal states of a system by examining its outputs. A system or application is considered observable if its current state can be inferred based on the information provided by what are known as the three pillars of observability: event logs, metrics, and traces.
- Event logs are records that track specific events chronologically. They are critical for characterizing the app’s behavior over time.
- Metrics are quantitative measurements, such as CPU utilization, request response times, and error rates, that provide numerical data about the performance and health of the app.
- Traces record the end-to-end flow of transactions through the app. They allow developers and architects to understand the interactions and dependencies between various components of the app.
Observability is crucial for the initial refactoring of monolithic legacy apps into microservices. A fundamental goal in the refactoring process is to maintain functional parity between the original application and its post-refactoring implementation in the cloud. That is, the refactored app should initially (before any updates or corrections are incorporated) function identically with the original monolith in all feasible operational scenarios.
Achieving functional parity depends on architects having deep insight into the performance and functioning of the original monolithic codebase. A high degree of observability is required to ensure that all functionalities and use cases of the original app are identified and appropriately addressed in the refactored implementation.
Once an app has been initially refactored and integrated into the cloud environment, observability becomes even more important. An app that’s been restructured to a cloud-native, microservice-based, distributed architecture is typically composed of many different components and services that, by design, function and scale independently of one another.
Although such apps are almost uniformly easier to understand conceptually than were their monolithic precursors, they also are more topologically and operationally complex and require an even greater depth of observability for developers to fully understand how the system is functioning.
Applying Observability in Continuous Refactoring
Architectural observability is a key element of the continuous refactoring process. It allows architects to identify, monitor, and fix application architecture anomalies on an iterative basis before they grow into bigger problems. The fundamental principle governing observability in app modernization is that comprehensive monitoring must be performed throughout the refactoring process so that developers have an in-depth view of the behavior and performance of their apps at every step.
Achieving comprehensive architectural observability involves a combination of static analyses and real-time operational monitoring that enables development teams to gain deep insights into their application’s structure, behavior, and performance at every stage of refactoring. Key performance indicators (KPIs) are defined and tracked, and monitored load and stress testing is conducted to identify potential bottlenecks and scaling challenges.
Architectural drift is detected by first establishing an initial architectural baseline that describes how the app functions in normal operation. Monitoring then continues as changes are detected in the architecture, allowing developers to proactively detect and correct issues that can lead to architectural drift. The baseline is reset and the monitoring procedure repeated at each step in the continuous refactoring process.
Tools for Observability in Continuous Refactoring
Attaining a high degree of observability requires the use of appropriate monitoring tools. Such tools must provide deep domain-driven observability through sophisticated static analyses, as well as dynamic tracking of process flows and dependency interactions during actual user activity or test scenarios.
A good observability tool will be capable of baselining, monitoring, and alerting on architectural drift issues such as:
- Dead Code: code that is accessible but no longer used by any current user flows in production.
- Service Creep: services are added, deleted, or modified in ways that no longer align with the established architectural design.
- Common Classes: commonly used functions are not all collected into a shared class library to reduce duplicate code and dependencies.
- Service Exclusivity: failure to ensure that each microservice has its own defined scope and is not unnecessarily interdependent with other services.
- High-Debt Classes: classes that have a high degree of technical debt due to elevated levels of complexity, functional issues or bugs, and difficulties in maintainability or adaptability.
A good example of an advanced observability tool that performs these functions at a high level is the vFunction Architectural Observability Manager. This solution allows architects to manage, monitor, and fix architectural drift issues on an iterative, continuous basis. Not only does it identify and track architectural anomalies, but it notifies developers and architects of them in real-time through common alert systems such as email, Slack, or the vFunction Notifications Center.If you’d like to know more about how a state-of-the-art tool can provide the architectural observability needed to incorporate continuous refactoring into your application modernization process, we can help. Contact vFunction today to see how.