Software development constantly evolves, and so do the libraries and frameworks we rely on. Keeping codebases up-to-date is crucial for long-term maintainability but often feels tedious and error-prone when done manually. Fortunately, tools like OpenRewrite offer an elegant solution: automating large-scale code changes in a reliable, repeatable way.
In a recent article, Philip Riecks walks through a practical example of how OpenRewrite can be used to modernize Java code. Specifically, he demonstrates how to update outdated Mockito usage — migrating from the legacy MockitoAnnotations.initMocks(this)
approach to the more modern @ExtendWith(MockitoExtension.class)
setup for JUnit 5.
This example highlights a few key points that are relevant not only for testing libraries but for any code migration project:
Why Automate Code Migrations?
Manual code migrations tend to be time-consuming and error-prone. Especially across large codebases, it's easy to introduce inconsistencies or regressions. Automating these changes ensures that:
- Transformations are consistent across the entire codebase.
- Human error is minimized, freeing up developer attention for more meaningful tasks.
- Changes are repeatable, allowing migrations to be re-run as needed (for example, when rebasing or merging branches).
These principles align well with modern software development values, favoring reproducibility, clarity, and continuous improvement.
How OpenRewrite Works
OpenRewrite structures migrations as recipes. Each recipe defines a set of search-and-replace operations, written in Java and AST-based (Abstract Syntax Tree), making them much more robust than simple text-based find-and-replace tools.
In Philip’s example, the migration involves:
- Finding classes that use
MockitoAnnotations.initMocks(this)
. - Replacing them with the appropriate
@ExtendWith(MockitoExtension.class)
annotation. - Cleaning up any now-redundant imports or method calls.
What's particularly powerful is that OpenRewrite doesn’t just naively replace text. It understands Java syntax and semantics, which makes it resilient even in more complex code scenarios.
The Developer Experience
Applying OpenRewrite recipes feels very natural once set up. Recipes can be run from the command line or integrated into build tools like Maven and Gradle. In many ways, it feels like using a smart IDE refactoring — just at a much larger scale.
Philip’s guide provides a minimal working example project that shows how little effort is needed to get started. If you're curious, I recommend checking out the full walkthrough here.
When and Where to Use It
OpenRewrite is particularly useful for:
- Library upgrades (e.g., JUnit 4 → JUnit 5, Spring Boot upgrades)
- API deprecations (e.g., moving away from obsolete methods)
- Codebase modernization (e.g., adopting Java 17 features)
Of course, like any automation tool, it requires some care and review. But the benefits far outweigh the initial learning curve, especially for teams maintaining growing, aging, or heavily-used Java applications.
Closing Thoughts
As with code reviews, automating migrations with OpenRewrite fosters a healthier, more sustainable development culture. It frees developers from repetitive, mechanical tasks, allowing them to focus on what truly matters: building better software collaboratively.
If you find yourself dreading the next big library upgrade, maybe it's time to give OpenRewrite a closer look.