Seamless Code Changes with Parallel Change
No broken builds when changing code anymore. Commit and merge to main at any time. Continue code changes whenever you have time.
So your code doesn’t compile anymore (again)?
I assume we all have been in this situation once in our software engineering career: we need to replace an old piece of code with a new one. This could be something as small as replacing a List
with a stronger domain type like CustomerAddresses
, or replacing an old SQL connection with a new NoSql database. Maybe you even need to replace an entire system with a new one.
Whatever the scale of the task, sooner than later, we find ourselves ripping out old code and trying to integrate our new code. Often, this is when the codebase does not compile anymore. Other teams may be pushing more features into main, which we’ll have to integrate into our non-compiling code mess. Thus, the complexity of the task increases continuously with every day we cannot merge our code.
Days may go by where we try to get our code to compile and our tests green again, but at some point, we may simply give up and revert it all. The complexity is just too high. If we cost 100$ an hour and spend 2 work weeks on it, we may have just burned through 8000$ or more in the process! That’s certainly not what our employer expects to pay for us for essentially accomplishing nothing at all!
Is there a better way to replace old with new that ensures our code always compiles, our tests remain always green, we can merge to main at any time, and continue changing our code at a later stage, should the product require us to work on something more urgent like a bug?
Parallel Change to the Rescue!
The technique that shines in replacing old with new code without breaking it in the process and which scales from variables to system level is called Parallel Change.
As far as I’m aware, the technique was famously illustrated by Joshua Kerievsky (Refactoring to Patterns/Industrial Logic), see e.g. here around the 21 minutes mark. I highly recommend you have a look at that presentation as well.
The Idea behind Parallel Change
The idea behind Parallel Change has its origins in construction work. Imagine the task to refurbish or replace an old road. You cannot simply close down the entire road for a year or two. Instead, cars will still need to be able to continue driving on the old road while the new one is being build, even though there may be slow downs. The goal is to keep traffic running while at the same time refurbish the old road.
This is exactly what we want to achieve with our system: the user should not experience a complete shutdown. Some delays may be acceptable, but the system should continue running while behind the scenes it is being changed. So let’s investigate how Parallel Change ensures our code can always be merged to main.
Parallel Change Workflow

Parallel Change works as follows:
Create the new piece of code you want to replace an old one with (with tests!).
Add the new piece of software right next to the old one.
For every write (command) to the old software, add a write to the new one, step-by-step, and run your tests after each addition.
Switch every read access (query) from the old piece of software to the new one, step-by-step, and run your tests after each switch.
Remove all the writes to the old software, step-by-step, and run your tests after each removal.
Remove the old piece of software.
Step 3 ensures the state in both old and new pieces of software is the same, while in step 4, we will find out if we actually have the same state in both pieces. Until step 3, nothing should break, while in step 4, we potentially have a problem if we forgot any of the writes, so we need to have a strong test suite to validate if we have broken anything. Always run this test suite after a change!
Parallel Change allows you to commit after every step and know that the system still works! You can integrate immediately and change the system over a long period of time, sometimes years, without breaking a sweat. You could even replace entire systems that way. Traffic continues to flow even though there may be some obstacles and slowdowns on the way. Let’s now see a real code example (see also the video).
Parallel Change with a Simple Code Example
To illustrate how you can apply Parallel Change in your codebase, I have prepared the following small example that you can also find in the repository.
public class Circle {
private int x;
private int y;
private int radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
public boolean contains(int x, int y) {
return (x - this.x) * (x - this.x)
+ (y - this.y) * (y - this.y)
<= radius * radius;
}
public void moveTo(int x, int y) {
this.x = x;
this.y = y;
}
public void resize(int radius) {
this.radius = radius;
}
public String format() {
return "circle: {" +
"\n\tcenter: (" + this.x + "," + this.y + ") " +
"\n\tradius: " + this.radius + "\n}";
}
}
The goal is to replace x
and y
with a stronger Point
record. The first step is to create and add a new instance of Point
right next to the coordinates as center
, see below.
public record Point(int x, int y) {}
public class Circle {
private int x;
private int y;
private Point center; // add center point
// ...
}
As a next step, we find all the places where x is written to and add an equivalent write operation to the point class. Because x
and y
always come in pairs, this will also solve writing the corresponding y
value. However, our initial focus is on x
. There are two places where x
is written to, one in the constructor and one in the moveTo
method, see below.
public class Circle {
private int x;
private int y;
private Point center;
// ...
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.center = new Point(x, y); // set center
// ...
}
// ...
public void moveTo(int x, int y) {
this.x = x;
this.y = y;
this.center = new Point(x, y); // set center
}
We run our tests after each step. Up until now, nothing should break as we only added code. The center
point should now always have the same state as x
(and by coincidence, also y
). In the next step, we will switch every call to get the value of x
to a call to center.x()
and run our tests every time we switch. Three places need to be adapted, two in the contains
method and 1 in the format
method.
public class Circle {
// ...
public boolean contains(int x, int y) {
return (x - this.center.x()) // read center.x()
* (x - this.center.x()) // read center.x()
+ (y - this.y) * (y - this.y)
<= radius * radius;
}
// ...
public String format() {
return "circle: {" +
"\n\tcenter: (" + this.center.x() // read center.x()
+ "," + this.y + ") " +
"\n\tradius: " + this.radius
+ "\n}";
}
}
Now, x
is not read from anymore, only from center.x()
. We can now step by step remove writes to x
, until the variable x
is entirely dead code, and finally remove it. The resulting code looks as follows:
public class Circle {
private int y;
private Point center;
private int radius;
public Circle(int x, int y, int radius) {
this.y = y;
this.center = new Point(x, y);
this.radius = radius;
}
public boolean contains(int x, int y) {
return (x - this.center.x()) * (x - this.center.x())
+ (y - this.y) * (y - this.y)
<= radius * radius;
}
public void moveTo(int x, int y) {
this.y = y;
this.center = new Point(x, y);
}
public void resize(int radius) {
this.radius = radius;
}
public String format() {
return "circle: {" +
"\n\tcenter: (" + this.center.x() + "," + this.y + ") "
+ "\n\tradius: " + this.radius
+ "\n}";
}
}
We can now follow the same process to get rid of y
. Step by step switch all reads from y
to center.y()
and run the tests after each step, then remove all the writes to y
, and finally, remove the member variable y
. Eventually, we arrive at the following code:
public class Circle {
private Point center;
private int radius;
public Circle(int x, int y, int radius) {
this.center = new Point(x, y);
this.radius = radius;
}
public boolean contains(int x, int y) {
return (x - this.center.x()) * (x - this.center.x())
+ (y - this.center.y()) * (y - this.center.y())
<= radius * radius;
}
public void moveTo(int x, int y) {
this.center = new Point(x, y);
}
public void resize(int radius) {
this.radius = radius;
}
public String format() {
return "circle: {" +
"\n\tcenter: ("
+ this.center.x() + ","
+ this.center.y() + ") " +
"\n\tradius: " + this.radius
+ "\n}";
}
}
Both x
and y
have been completely removed, the code always compiled, the tests where always green. We could commit at every step of the way, go home, merge into main, and nothing would have broken our code.
This algorithm scales from small examples to system levels and can also be used for example for database migrations. Give it a try!
If you want another example to practice, you could try to replace the radius
variable with a strong type Radius
. Have a look at the repository and the solution on the branch solution_parallel_change
.
How do I realise I need Parallel Change?
When I first started with Parallel Change, I didn’t realise it’s full potential until I found myself time and time again in a broken system. Whenever it felt like “I never get that software to compile again”, I realised that I forgot about Parallel Change. So, the next time you find yourself in a situation where you end up with non-compiling code and analysis paralysis, remember not to get any SADER (pun intended ;)):
Set up the new structure
Add parallel writes to the new structure
Divert reads from the old to the new structure
End writes to the old structure
Remove the old structure
Parallel Change will transform the way you approach complex code changes!
Have you experienced similar issues while changing code? What was your strategy to get over the issues of non-compiling code for a long time?
If you like content like that, don’t forget to subscribe to stay up to date!
Want to learn how to apply Parallel Change in Classic TDD to get from 1 to many? Check out our comprehensive O’Reilly course that demonstrates exactly that.
Want to learn how to write evolvable code? Visit our website www.codeartify.com and get in touch with us! We provide worldwide remote and on-site workshops for you and your team!