Branch by Abstraction with Example
A way to refactor or change dependencies in your code and don't mess it up

What is Branch by Abstraction?
Is a technique for making a large-scale change to a software system in a gradual way that allows you to release the system regularly while the change is still in-progress. Martin Fowler
Let's imagine this scenario
We have a weather system (classic example), but our system uses a legacy service that we gonna call 'LegacyWeatherService', the code implemented in this system cannot be refactored also is kind of obsolete, but still work, maybe not in the better way.
Also, you have some actions in your system that use that service, let's see the following image:

If we write the code that represents this image, could be something like this
class GetTodayWeather {
private final LegacyWeatherService legacyWeatherService;
public GetTodayWeather (LegacyWeatherService legacyWeatherService) {
this.legacyWeatherService = legacyWeatherService;
}
public Weather execute() {
Weather weather = this.legacyWeatherService.getFor(System.currentTimeInMilliseconds())
return weather;
}
}
The other actions could be similar to this one
So, How can we replace the legacy code but doing it in fragments?
For doing that we must create an abstraction layer first (Yeah, now the name 'branch by abstraction' makes sense), the idea is, the actions must not know the real implementation, they must only know that there is some kind of service that provides the information that they need.
So first step is to create the abstraction, it could be an interface or abstract class, and then connect that abstraction with our legacy code.

Once again if we need to put that image in code, could be something like this
// Interface
interface WeatherService {
Weather from(long time);
}
//Legacy Weather Service
class LegacyWeatherService implements WeatherService {
public Weather from(long time) {
// legacy code here...
}
}
// Action
class GetTodayWeather {
private final WeatherService weatherService;
public GetTodayWeather (WeatherService weatherService) {
this.weatherService = weatherService;
}
public Weather execute() {
Weather weather = this.weatherService.from(System.currentTimeMilliseconds())
return weather;
}
}
Why we talk about 'Branch'?
That is because your system is being used right now, so you can not stop the service and must implement the new functionalities soon as possible, so if you finish creating your abstraction layer, now you can create the new implementation and migrate the calls from the legacy code to the new service, but doing it by action and deploying every time you update one action.

// Interface
interface WeatherService {
Weather from(long time);
}
//Legacy Weather Service
class LegacyWeatherService implements WeatherService {
public Weather from(long time) {
// legacy code here...
}
}
//New Weather Service
class BetterWeatherService implements WeatherService {
public Weather from(long time) {
// Good code here
}
}
// Action
class GetTodayWeather {
private final WeatherService weatherService;
public GetTodayWeather (WeatherService weatherService) {
this.weatherService = weatherService;
}
public Weather execute() {
Weather weather = this.weatherService.from(System.currentTimeMilliseconds())
return weather;
}
}
// Then when you create your action, pass the new implementation
...
GetTodayWeather getTodayWeather = new GetTodayWeather(new BetterWeatherService())
...
You continue making the new implementation in your other actions, once you finish your system must be like this.

As you can see, your legacy code has no reference now, nobody is using it, which means you can delete that implementation, with all these implementations we gain another thing, now the actions don't know any concrete implementation, so if we have to add another service implementation we already could use the abstraction layer created before.
