May 2017

Jack Reeve introduced the concept that source code is the design and programming is about designing software.1 As software grows, the design, or architecture, tends to grow large and complex. This is because software architecture is constantly evolving, making software maintenance difficult and error-prone. In this article, we will talk about symptoms of bad architecture and how to fix them.

Poor Software Architecture

According to Robert Martin2, there are seven symptoms of poor architecture.

Rigidity: this means the system is hard to change. Every change forces other changes to be made. The more modules that must be changed, the more rigid the architecture. This slows down development as changes take longer than expected because the impact of a change can not be forecast (impact analysis can help). System stability and average impact are good architecture metrics to monitor for rigidity. System stability measures the percentage of elements (on the average) that would not be affected by a change to an element. Average impact for an element is calculated as the total number of elements that could be affected if a change is made to this element (or the transitive closure of all elements that could be affected).

Fragility: when a change is made to the system, bugs appear in places that have no relationship to the part that was changed. This leads to modules that get worse the more you try to fix them. In this case, these modules need to be redesigned or refactored. Cyclicality metrics can help find fragile modules. Cyclicality is useful in determining how many elements of a system are in cycles. See our blog post “Cyclicality and Bugs” for more information.

Immobility: this is when a component cannot be easily extracted from a system, making it unable to be reused in other systems. If a module is found that would be useful in other systems, it cannot be used because the effort and risk are too great. This is becoming a significant problem as companies move to microservices and cloud-ready applications. A metric that is useful in this case is called coupling. Coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are and the strength of the relationship between modules.

Viscosity: this is when the architecture of the software is hard to preserve. Doing the right thing is harder than doing the wrong thing (breaking the architecture). The software architecture should be created so it is easy to preserve the design.

Needless complexity: the architecture contains infrastructure that adds no direct benefit. It is tempting to try to prepare for any contingency, but preparing for too many contingencies makes the software more complex and harder to understand. Architectures shouldn’t contain elements that aren’t currently useful. Cyclomatic complexity metrics can help diagnose this problem.

Needless repetition: this is when an architecture contains code structures that are repeated, usually by cut and paste, that instead should be unified under a single abstraction. When there is redundant code in software, the job of changing the software becomes complex. If a defect is found in code that has been repeated, the fix has to be implemented in every repetition. However, each repetition might be slightly different.

Opacity: this is when the source code is hard to read and understand. If source code is the design, this is source code that does not express its intent very well. In this case, a concerted effort to refactor code must be made so that future readers can understand it. Code reviews can help in this situation.

Summary

While source code may be the design, trying to figure out the architecture from the source code can be a daunting experience. Using architectural analysis tools like Lattix Architect can help by visualizing the dependencies. This allows you to refactor the architecture, prevent future architectural erosion, and provide metrics like system stability, average impact, cyclicality, coupling, and cyclomatic complexity.

What do you do when your boss asks you how long an enhancement or change request will take to implement? Do you use impact analysis? If not, it might be worth using it next time to develop a reliable estimate.

Impact analysis is “identifying the potential consequences of a change, or estimating what needs to be modified to accomplish a change." 1 The major goal of impact analysis in software is to identify the source code affected by the proposed change. As software becomes increasingly large and complex, there is an ever increasing need to predict and control the effect of software changes. Not accurately estimating the size and complexity of the change can lead to poor estimates, delays in release schedules, architectural erosion, and unreliable software products.

Why is impact analysis important?

An impact analysis will confirm which part of the application you need to change, and tell you how much of the application may be affected by the change. Understanding the change impact enables teams to quickly and accurately respond to change requests. With impact analysis, teams can be responsive while maintaining control over scope and customer expectations. Impact analysis is essential on projects where quality and safety are an issue such as in medical devices, automotive, and aerospace. Impact analysis helps developers calculate the impact of change.

Types of impact analysis

Two major technology areas for impact analysis are traceability analysis and dependency analysis. Traceability analysis examines the links between requirements, specifications, design elements, and tests. These relationships can be then analyzed to determine the scope of an initiating change. This addresses the impact from a broader perspective. Dependency analysis involves examining the linkage between program elements (variables, logic, modules, etc.) and determining the consequences of initiating a change in the source code. It provides a detailed evaluation of low-level dependencies in the code.

How to do an impact analysis

An impact analysis examines the proposed change and identifies components that might have to be created, modified, or discarded and estimates the effort associated with implementing the proposed change. There are generally three steps to do an impact analysis:

Identify all files, classes, and methods that would be involved if this change were to be implemented.

Understand all the possible implications of making the change. When a change is made there is often a ripple effect, so examining multiple levels of impact (ideally to transitive closure; see below) is ideal.

(Optionally) Restructure your code to reduce the impact of the change

Summary

On complex projects with thousands of elements, manually determining what and who is affected by change is time consuming and error prone. Lattix Architect creates a report of what is affected when a change occurs. Impact analysis in Lattix Architect helps you manage change and lets you review a change’s impact before the change is made. If you are interested in learning more about Lattix Architect’s impact analysis, request a free trial today.

Lattix Architect and Lattix Web are great complements to your static analysis solution. If you have a software quality initiative, a clean architecture is essential. Decreasing the complexity of your software allows you to get better results from your static analysis tools. By understanding your software dependencies and coupling, you can pinpoint areas that need focus for testing.

Clean architecture leads to better quality

Since problems in architecture do not directly point to software bugs, there is often a tendency to fix bugs without regard to the overall design. If the tests work and the software functions correctly, why worry about the architecture? As a result, architecture erodes over time and the original design intent is lost. Software modules get coupled and become hard to understand and maintain. This leads to poor quality software. The consequences of software coupling have been documented in a number of research studies. Read our whitepaper “Business Value of a Clean Architecture” for more information.

Indeed, keeping the architecture clean may be one of the best ways to reduce defects and vulnerabilities and improve the maintainability of your software. Wipro, using Lattix Architect to monitor their architecture, observed a 56% reduction in the number of bugs, as well as a 45% productivity improvement in fixing bugs.1

Lower complexity means better static analysis results

The paper “Measuring the Effect of Code Complexity on Static Analysis Results” shows that detection rates of static analysis solutions decreased with increasing code complexity. Complex programs are more likely to contain unusual code constructs. This causes problems for static analysis tools not designed to handle these situations. One way to lower complexity is to use a tool like Lattix Architect to understand the dependencies and coupling between modules. Once you have that understanding, you can start to improve problem areas and thereby lower complexity.

Pinpoint areas of focus (many dependencies or highly coupled)

Understanding the software architecture helps you pinpoint areas that you should focus on for testing, including static analysis testing. If you see a module or namespace with many dependencies, it’s a good candidate for additional testing. The module or system can affect many different areas and would therefore have a high impact on the rest of the code.

The other areas of interest are tightly coupled systems. Tightly coupled systems tend to exhibit a number of negative characteristics. A change in one module in a tightly coupled system usually forces a ripple effect of changes across other modules. Creating new modules in a tightly coupled system is hard because of the increased dependencies. A particular module might be harder to reuse and/or test because dependent modules must be included.

Summary

Understanding your software architecture maximizes the effectiveness of your static analysis solution. If you are interested in understanding your software architecture, check out Lattix Architect. We also have integrations with Klocwork and Understand that allow you to import Klocwork/Understand data directly into Lattix Architect.

Why is modularity important in agile development?

Modularity is a key ingredient of agile architecture. Change is a major part of agile development. As the Agile Manifesto states: “Welcome change requirements, even late in development agile processes harness change for the customer’s competitive advantage.” Modularity allows development teams to envision and manage change more easily because it separates a program into components that contain functionality that is independent and interchangeable. Many organizations that follow an agile process do so without considering the structure of their application. When business requirements change for a project, the team struggles to adapt. The flexibility of modularity combined with continuous integration allows for quick identification and resolution of any architectural issues or shifts in the code.

As an example, let’s say that an agile team is told that they need to use a library from Vendor X as part of their development. The project becomes tightly coupled to this library. If the structure of the application is not monitored and not modular, it’s hard to identify areas of the system that talk to this library. The business then tells you that Vendor X is out and you need to use the library from Vendor Y. How easy is it to remove Vendor X and replace it with Vendor Y? If all of the code that depended on the library from Vendor X was isolated in a single module, it would be pretty easy. To handle change, modularity is necessary. Since agile welcomes change, modularity is necessary for agile architecture.

Modularity is also important for Agile Parallel development. Agile Parallel development is when two components that have no dependencies on each other are able to be worked on at the same time instead of waiting on other teams to finish their components. This dramatically increases developer productivity. By removing the constraints (dependencies) with modularity, you empower teams to work in parallel. According to CA Technologies, Agile Parallel Development enables up to 90% more defects to be detected and up to a 50% reduction in a typical development schedule.

In large code bases, it becomes harder to manage the architecture at a code level to ensure modularity. Is it easier to understand the impact of change when examining a system of 10,000 classes or a system with 10 classes? Clearly the latter. A higher level visualization and analysis tool like Lattix Architect is needed to understand and manage the dependencies between components, which is critical to modularity and accommodating change. To get an idea of how modular your code is and/or how you can make it more modular, check out Lattix Architect.