lundi 7 novembre 2022

Breaking out of legacy with 3P

Protect, Prepare, Produce - 3P. A recipe to break out of legacy

If you prefer you can read this in French 

Photo by Ugne Vasyliute on Unsplash

Maybe you've already had experiences like

  • A refactoring project over several months was been cancelled
  • Tests that don't really help, perhaps even slowing you down
  • Refactorings that don't turn out that good 
  • Code that continuously degrades, despite the stories dedicated to refactoring and testing that you do regularly.

If this is the case you would probably be interested in knowing why these efforts are not bringing the expected benefit and why the 3Ps would help you.

Let's first consider the classic approaches of either doing refactoring and testing in dedicated stories, or doing most of the refactoring and testing at the end of the story. These approaches seem attractive at first glance, but they have a big ROI problem. Indeed, they lengthen the time between investment (refactoring) and return on investment (a new feature, facilitated by said refactoring). 

Precisely, when we refactor in a separate project or story, or even at the end of a story: who reaps the value, and when

It's the next person who touches the code, hopefully, sometime later. And that's provided that the refactoring actually turns out to facilitate the future work that we don't know beforehand... Basically the ROI will be for someone else, maybe. And this in a potentially distant future. Not ideal, but we can notice that the ROI is closer if we do it at the end of the story than if we do it in a separate project.

Same question for tests. Who reaps the value of tests written after the fact? And when will that be?

The answer is essentially the same. It's the next person who touches the code, hopefully, sometime in the future. Provided that the tests are still relevant with regard to the new need, which we didn't know about when we wrote the tests.

In short, we can see that there is a time that can be more or less long between the investment and the benefit. The shorter the delay, the better. But that's not all. In my experience, we don't always do the ideal refactoring and we don't always do the ideal tests. One of the reasons is that we don't know the future very well. Could we minimise these "mistakes"?

So we should find a way to get feedback on the quality of our tests and our refactoring as quickly as possible, and to minimise the uncertainty about the future. If we did this, we'd have refactoring and tests  that would bring much more for the same amount of time invested. Given that our investments pays off better, we're likely to increase the investments, possibly by a lot!


This is where the idea of the 3Ps comes in, where we not only bring the investment closer to the benefit, but also we are the foremost beneficiaries of the tests just wrote and the refactoring we just did! This has the added benefit that any ill conceived tests and refactoring are immediately brought to the attention of just the right person to fix the issue.  

The idea is simple, for each story break down the work into 

  1. Protect
  2. Prepare
  3. Produce


Setting up the missing tests

First we have to protect the changes with tests. To do this, we need to identify the code that needs to be touched in order to add any missing tests (for the upcoming refactoring). We don't worry about covering anything but the code that will be changed, that would be a distraction

The code that will be affected must be perfectly covered with tests. Any less and we wouldn't dare to thoroughly refactor in the next phase. However, we can completely ignore the maintainability of the tests, we're only concerned with protecting the refactoring phase. So this phase is way shorter than what we might expect.

We need tests that will not break during refactoring, so low-level tests are not always the best. Typically, duplicating and adapting some high level tests, even end-to-end tests, works quite well. Quite often the code is not testable at all, but with high level tests anything is testable.


Refactor in depth to make the code fit for the new feature. Address testability

We make sure that the new functionality can be inserted in the code in an elegant way. 

We also make the code easily testable. Maybe we wrote end-to-end tests in the protect phase. This is the time to refactor to move those tests to a level where they're fast, predictable and easy to understand. Basically improve maintainability of the tests without sacrificing much of confidence and refactorability.


Code the new functionality in TDD

Since the code is clean and testable, this phase can be done using TDD, regardless of the code at the start.


With the 3Ps we write the tests, to do ourselves the refactoring behind. We minimize the time between refactoring and introducing the new feature. We know the functionality before we start. We can work in TDD to benefit from the new tests ourselves. How could the ROI be better? And if it is, wouldn't we do more of it, a lot more?

End notes

I'm not saying to avoid refactoring at the end of the story, just that if that's all we're doing, we're missing a lot.  

I'm aware don't go into much detail about how the 3Ps work in practice. I'll try to make it clearer in a subsequent post.

Translated (mostly) with Originally posted in French

Aucun commentaire:

Enregistrer un commentaire