Evolution of Architecture-3-Refactor or Rewrite

Huseyin Kutluca
Software Architecture Foundations
6 min readJun 18, 2021
Prag

In the previous article “How to pay technical debt?”, we mentioned that the technical debt will be paid with the refactoring. Refactoring can be at the code level as well as at the architectural level.

Code technical debts are usually detected with tools such as sonar qube, cppcheck, or peer reviews and resolved at the package, class, or method level. In code refactoring, the structure and interrelationships of components remain untouched. In architectural refactoring, the aim will be to improve the system’s quality attributes such as performance, testability, maintainability, and availability. Architectural refactoring includes,

  • Breaking dependencies between components,
  • New component extraction,
  • Re-implementing violated architectural principles
  • Improving the thread structure,
  • Replacing an old technology with a new technology,
  • Switching to a new version of a used library,
  • Adding security-related capabilities,
  • Improving the logging infrastructure and applying the same principles in all components,
  • Changing the data model and communication interface in order to comply with the standards,
  • Getting rid of financially burdensome 3rd party libraries and technologies,

Refactoring to Microservices

Today, many companies are switching to a microservice architecture because the existing product is more modern and more manageable. Some companies are gradually reorganizing the existing system and transitioning to microservice architecture. Others leave the existing structure and transition to microservices with rewrite. Martin Fowler proposes a gradual transition with refactoring. He explained the subject with the aphorism, “Rewriting in the form of a big bang causes a big bang”. By its nature, transition is an architectural reorganization approach.
Suggested approaches for transitioning to microservice architecture are listed below:
New services: It is based on developing the newly developed capability as a micro service and ensuring its integration with the existing architecture. This way, the team migrates the existing software incrementally with minimal risk. In this way, familiarity with technology and new structures is gained.
Separating user interface and application components: This approach is primarily based on separating the user interface and algorithms. Between these two components is REST etc. communication is established. This is not a permanent solution, but an interim solution.
Extract Services: It is a method of developing a business area that is a candidate to be a microservice in the existing integrated architecture as a microservice and running it with existing software. Gradually, the integrated legacy software will migrate to the microservice.

How to Execute Refactoring

This activity is not only in a local component, as in code refactoring, but includes basic processes such as analysis, design, planning, implementation, and testing.
Analysis
Architectural reorganization is a top down effort. Component partitioning is about interfaces and dependencies between components. We can reconsider the architectural decisions at the beginning of the project, as we know better both the technology and the business area in the current situation to perform architectural reorganizations.
In order to better understand the existing architecture, it is necessary to examine the high-level architectural drawings and if these drawings are not available or out of date, they should be redrawn. Making a dependency matrix between components is also a recommended approach. This makes it easier to understand the system and what needs to be changed.
Planning
In the first lesson of software architecture education, we said that it includes difficult decisions to change while defining the architecture. In this case, it should be accepted from the outset that the refactor activity will bring a certain difficulty, and if possible, rearrangements should be made iteratively with an evolutionary approach rather than a revolutionary approach.
Decide what architectural changes to make. Decide what improvements to make, taking into account technical and budgetary limits. Determine the return on investment with measurable metrics. If the refactoring is too much, plan the transition. Don’t spread the refactoring work to too many people.
Plan the architectural reorganization in iterations. According to the scenario approach, plan the current state of the software and its evolutions in each iteration. Determine the purpose of the iteration. Determine the benefit you will get from that.
It can be based on replacing old components with a new one at a time. In this approach, adaptation of other components to the reorganized component is provided.
In the adaptation approach, it involves reorganizing the relevant module rather than completely rewriting it. Others using the component related to this approach need not change.
After you decide to reorganize the architecture, present it to senior management. Instead of discussing why the existing software is faulty, highlight the benefits of the new solution and the benefits from this refactoring. Never say the current architecture is too bad! Your goal should be to improve the product, not to criticize what has been done. Remember that every software design is created with the constraints, know-how and technical solutions existed at the time it was created.

Realization
Architectural refactoring is an activity that requires experience. Developers should thoroughly understand the change to be made and the system architecture. The software architect should meet with the team to reveal the purpose and sensitive points of the refactor. At the beginning of the work, the vision for the architecture that is planned to be achieved last should be clearly stated.
Make sure you don’t break other quality attribute requirements when doing this refactoring. For example, when you divide it into sub-components to increase modifiability, make sure that the system still meets the expected performance and does not give any security-related vulnerabilities.
One of the most effective weapons in refactoring is repeated run tests. With these tests, it can be guaranteed that existing features are preserved after refactoring.
Don’t forget to update the design document with the refactoring.

Rewrite the Project

Companies sometimes choose to sit down and rewrite existing software rather than refactoring it. This is if the existing architecture has become too complex to fix (technical bankruptcy) then rewrite may make sense. Similarly, rewriting will make sense if the technology of the software and the dependent libraries and hardware are no longer supported and create a huge burden on us.
Rewriting can be meaningful if we can easily transfer the domain knowledge from the previous software to the new project, and if we take into account the lessons learned in the previous project. Let’s assume that we could not create the ideal architecture because we did not know the technology well in the previous project. How well do we know the attractive new fashion technologies that are in front of us when we decide to rewrite, and we need to be sure how successful they will be in solving the problems of our business. The whole world is using these technologies then that must be a good thing it is not a concrete answer. Similarly, we are expected to know the quality attributes requirements of the previous architecture well enough and to meet them with the new architecture. In fact, considering new quality attributes that were not taken into account in the previous architecture and producing solutions will increase our chances of success.
Developers will always prefer to rewrite existing software rather than understand and refactor it. But what is the guarantee that the rewritten software will not have as much technical debt as the previous one? This time, you will be expected to develop the same software again with more limited time and resources.
What is our goal in rewrite? Is it to rewrite existing features with new programming language and technologies or to develop more capable software according to evolving needs? The answer to this question should also be answered during the rewrite decision.
The risk of rewrite is that the outcome is not fully known. We should analyze what is the risk of failure of the product we will get after rewriting. If the rewriter fails or doesn’t finish on time, we should have a plan B. Perhaps in parallel, the old system should be continued to be used only by solving critical problems.
We must also be prepared for the time it will take for the rewritten product to mature and correct any errors when it becomes available. Top management and customers should be aware of this process and be prepared for it.

--

--

Huseyin Kutluca
Software Architecture Foundations

Highly motivated Software Architect with hands-on experience in design and development of mission critical distributed systems.