API versioning in Spring Boot, how to avoid code duplication (SonarQube Code Duplication)?

1 week ago 9
ARTICLE AD BOX

I have a microservice on Spring Boot. Very often we need to release a second version of the API (v2) that differs only slightly from the first one (v1). For example, just by changing a flag passed to the business logic, while the main request processing flow stays almost the same.

It usually starts like this:

@RestController @RequestMapping("/api/v1") public class OldControllerV1 { private final MyService service; @PostMapping("/process") public ResultDto process(@RequestBody RequestDto request) { // ... lots of different validation logic ... service.execute(request); } }

When people are in a hurry to ship a release, they often just copy-paste the code and create a similar controller for v2, passing an extra parameter to the service layer (in our case - a simple boolean to tell whether it's the old version).

It ends up looking something like this:

@RestController @RequestMapping("/api/v1") public class OldControllerV1 { private final MyService service; @PostMapping("/process") public ResultDto process(@RequestBody RequestDto request) { // ... lots of common validation logic ... service.execute(request, true); // true == old mode } } @RestController @RequestMapping("/api/v2") public class NewControllerV2 { private final MyService service; @PostMapping("/process") public ResultDto process(@RequestBody RequestDto request) { // ... the same validation logic copied from v1, maybe slightly different ... // The only real difference: service.execute(request, false); // false == new mode } }

At this point the CI pipeline usually fails (on the static analysis step). If the project has SonarQube with strict rules (Quality Gate), it blocks the build because of Code Duplication - the method bodies are almost identical.

That's actually fair - such duplication breaks clean code principles and makes future changes painful since you have to edit multiple places.

So the question is:

How can we refactor this code properly so that we:

Eliminate code duplication Keep Spring annotations working correctly Make the solution extensible (easy to add v3, v4, … later)
Read Entire Article