Incremental Software Development Strategies for Large Scale Refactoring #4 : a Pattern Language

6 minute read

It can sometimes be a real challenge to integrate, let alone deploy, a refactoring step by step ! Here are some patterns that make this easier.

This is the ninth post in a series about how to get sponsorship for large scale refactoring. If you haven’t, I encourage you to start from the beginning. It’s also the fourth about incremental software development strategies for large scale refactoring. My point is that it’s not possible to sell a refactoring to business people until we master those :

  1. How to find the time to refactor in our daily work
  2. How to learn to work in baby steps
  3. How to organize and manage this ongoing effort as a team

What about when it is not easy to split the work into incremental steps ?

Do you remember the DSL parser refactoring story in mentioned in another post ? Switching to a different parser technology incrementally sounds like an impossible mission. Even so, that’s what we did ! 

Here’s another story. A long time ago, I was working in a bank. We were to migrate imperative C++ financial contract models into declarative definitions in C#. We had to maintain an ugly adaptation layer. It made it possible migrate and deliver step by step. In the end, we suffered almost no bugs resulting from this transition.

Drawing of the pattern for a T-shirt. Patterns can be useful for Incremental Software Development of Large Scale Refactoring

Why the effort ?

Incremental refactoring implies going through Frankensteinesk intermediate situations. Situations where both the legacy and the new models exist in the software at the same time. This kind of adaptation layer costs time and energy, but doesn’t add value to the product either ! What’s the point of going through this ? Isn’t a Big-Bang change cheaper ? Here is why it is still worth doing :

  1. It’s safer. With incremental delivery, we confirm that what we are doing is working in production. On top of that, if something goes wrong, as we only delivered a small increment, the problem is easier to diagnose.
  2. It’s also safer in term of priorities. The system keeps working during all the refactoring. There’s no pressure to finish it before we can move on to the next ‘valuable’ feature. As I explained before it makes it possible to pause, and why not stop there for the moment. This can be helpful if we hit a new urgent priority.
  3. Finally, it creates value earlier. Instead of having to wait 2 months to get all the value, you start getting a bit of this value every week. Even refactoring create value ! They reduce the time wasted to fix bugs. They increase our productivity. Sometimes, they even improve Non Functional Requirements of the system.

Plot of value over time in incremental vs big-bang. We can see that incremental software development creates more value.

As we can see, the goal of incremental refactoring is not only to work in small steps. We also want to find a way to deliver value incrementally !

💡 Incremental refactoring is not only about baby steps, it’s also about early value delivery !

A Mini pattern language

As I said earlier, this is easier said than done. Some piece of code won’t let you refactor them step by step easily. Some will be too obscure to know where to start. Some will be just huge. Others will depend on an all encompassing third party. etc.

Here is a short pattern language to deliver large scale refactorings incrementally.

Discuss with a domain experts

Goal

We need to refactor code containing a lot of domain knowledge

Conditions

We have a domain expert available

Therefore

Have regular discussions with the domain expert to find the best modeling possible.

Consequences

  • 💚 We get simpler code than by trying to replicate the twisted legacy logic
  • 💚 Can save a lot of work by skipping deprecated aspects.
  • 💚 Chance to get bug fixes or new features for free
  • ⚠️ The system does not exactly behave as it used to, which can cause integration problems

Difficulties

It can sometimes be difficult to find a domain expert …

A lot of the presentations at the July Paris DDD Meetup were about how to find domain experts. Who actually seem to be pretty rare beasts ! Here are my notes.

Mind map of my notes at July Paris DDD Meetup. The point was how to find a domain expert

Bubble context

Drawing of developer and business expert inflating a bubble together. The bubble context is a way to incrementally grow a large scale refactoring from within the software

Goal

We want to refactor a large piece of code with no Big-Bang

Conditions

We have access to the internals of the code to refactor

Therefore

  • Create a new bubble of clean code (a namespace, a package …)
  • Rewrite a piece of legacy code in the bubble
  • From the legacy code, delegate to the bubble
  • Repeat until the legacy code is not used anymore

Consequences

  • 💚 Enables a step by step continuous delivery of the new version
  • 💚 It is possible to eventually transform the API of the system
  • 💚 It’s easy to add new features in the bubble long before the refactoring is over

Difficulties

  • Need to understand the legacy enough to find good delegation points
  • Need to understand what the old small piece of code was doing to re-write it. A domain expert might be mandatory.

Strangler

The bubble context grows from the inside, but the strangler starts from the outside.

Goal

We want to refactor a large piece of code with no Big-Bang

Conditions

We can keep the same interface (API) for the legacy and the refactored versions

Therefore

  • Wrap the existing code
  • Re-implement calls in the wrapper
  • Delegate the rest to the legacy
  • Repeat until you support all the interface
  • Remove the legacy code

Consequences

  • 💚 Enables a step by step continuous delivery of the new version
  • ⚠️ Maintenance of the wrapper and both versions of the code during all the refactoring

Difficulties

  • Interaction between the legacy and the refactored version is not always as simple. For example when the wrapped code is stateful
  • The granularity of the steps is the (method) calls to the interface. They need to be small enough for the whole process to be incremental

Remember my story about how we switched our DSL parser to ANTLR ? We used a Strangler to do this.

Drawings of a strangler plant growing around legacy code. The strangler is very effective to do incremental software development of doing large scale refactorings

Feature toggles

Sometimes, we just don’t find a way to deliver a refactoring to users step by step. I’ve seen situations where all incremental paths implied a temporary impact on NFRs. Ex : Keeping both versions of the code implied an increase in resource consumption.

Drawings of an electric switch. Feature toggles are sometimes the last resort to do incremental software development of large scale refactoring

Goal

Incrementally build a refactoring that we cannot deliver piece by piece to all our users.

Conditions

When we cannot find a way to incrementally deliver our refactoring to bulk of our users

Therefore

From the code, dynamically switch to the different versions depending on runtime configuration. This way, we let most users stick to the legacy version. Yet, we can build, test, integrate and deploy the new version to beta testers.

Consequences

  • 💚 We can build, integrate and test our refactored code in baby steps
  • 💚 We can beta and A/B test our refactored code
  • ⚠️ We need to maintain and evolve both versions of the code for a long time
  • ⚠️ We need to maintain the switches in the code
  • ⚠️ We only deploy to beta testers, and don’t get as much early value
  • ⚠️ Duplicate the Continuous Delivery pipeline to test different feature toggle sets

Difficulties

Maintaining feature toggles is a mess. Thus, we need to

  • As much as possible, prepare the code to reduce the number of switches. Ref : Branch by abstraction
  • Hunt down the number of active feature toggles at any given time
  • Reduce the scope of toggles. Where possible, we should push things out of the toggle into stranglers or bubbles.

Feature toggles are an alternate to branches. Even if toggles are painful to use, branches are worse ! I’m not going to go over branches. If you want to see why we should not use branches, check this talk.

💡 Feature toggles are painful, but branches are worse !

Final word

I’m done writing about Incremental Software Development Strategies for Large Scale Refactoring. This is only what I currently know about this very important topic. There’s one last thing we need to do to be successful at it. We all need to keep an eye on new ideas from the community, and to share this with our teams as much as possible.

This was the ninth post in a series about how to get sponsorship for large scale refactoring. If you haven’t start by the beginning ! In next post, I’ll start to go over how to present a refactoring in financial terms to business people.

Comments