Last year we explored Error Prone [1] and Picnic’s journey [2] developing an open source repository built around this static analysis tool. Open-sourcing a project often leads to unexpected opportunities, and for us, it sparked an exciting collaboration with Moderne, the company behind OpenRewrite [3]. Moderne has built upon Picnic’s Error Prone Support project in ways we hadn’t imagined, creating a bridge between Error Prone Support’s Refaster rules and OpenRewrite’s transformation capabilities. In this post, we’ll tell you the story of how a conversation in the backseat of a cab resulted in a successful collaboration between Picnic and Moderne–and where it’s headed next.
How did we get here?
In 2022, at Devoxx Belgium, we announced [4] that Picnic had open-sourced Error Prone Support. It’s a plugin built on top of Error Prone that provides automated code improvements through extensions and its extensive set of custom rules. Following the announcement, Rick began presenting at conferences and meetups to share the journey of Error Prone Support and Refaster. During these events, he often crossed paths with Tim te Beek, a contributor to OpenRewrite–an open-source tool that allows you to upgrade, improve and secure your applications automatically. Tim brought valuable insights to our conversations, sparking deeper discussions.
Over time, our conversations naturally turned towards the strengths of both tools. One topic stood out: the simplicity of Refaster and its potential combined with OpenRewrite’s capabilities. We started brainstorming ways to connect the two. These ideas came together in 2022 at Spring I/O in Barcelona, where we decided to collaborate and explore this exciting opportunity together. Our vision? To combine the expressive power of Refaster with the broadened scope and scale of OpenRewrite.
Initial Development Phase
The initial focus was to integrate support for Refaster rules, enabling both OpenRewrite and the Moderne platform to run them. Development began with a minimal shared API, focusing solely on the core elements: the @BeforeTemplate and @AfterTemplate annotations. These Refaster constructs allow you to define rewrite rules in concrete Java syntax.
Starting small, the team tested the concept using simple Refaster rule examples. The goal was to build a lightweight, proof-of-concept implementation capable of running a few recipes via OpenRewrite to validate the integration.
class StringIsEmptyRules {
@BeforeTemplate
boolean lengthIsZero(String s) {
return s.length() == 0;
}
@BeforeTemplate
boolean equalsEmptyString(String s) {
return s.equals("");
}
@AfterTemplate
boolean isEmpty(String s) {
return s.isEmpty();
}
}
Around Christmas 2023, Tim began experimenting with this integration on the OpenRewrite side as a passion project. He succeeded in getting a basic version up and running, applying initial Refaster rules. With significant contributions from both Tim and Knut Wannheden, they developed an annotation processor for OpenRewrite [5], which supported an initial batch of Refaster rules from Error Prone Support.
To finalize the integration, Tim submitted a pull request to Error Prone Support [6], introducing the OpenRewrite annotation processor. Since OpenRewrite requires Java 8 compatibility, the Error Prone Support build was tweaked to produce a separate recipes JAR. This tweak allowed recipes to be bundled and applied broadly across diverse environments. As a result, Refaster rules could now be applied to Java 8 codebases, even though Error Prone Support’s actual JDK baseline is 17.
With this foundational setup, Refaster rules could now run on the Moderne platform, enabling anyone to apply these rules to open-source codebases in seconds. This milestone marked a significant first step in combining the strengths of Error Prone Support and OpenRewrite. OpenRewrite migrations now increasingly leverage Picnic maintained rule sets.
Expansion of the Collaboration
After the initial support for Refaster was added in OpenRewrite, we immediately saw the benefits of having this integration. We could now run a single command for both OpenRewrite and Refaster, without having to change any build files. On top of that, we could now run recipes on a very large scale, to spot check the results, and even create pull requests against Open Source projects. An example is a pull request where we applied AssertJ best practices on a Spring Project repository [7], containing changes of both OpenRewrite and Refaster.
Direct communication between our teams was invaluable during this phase–it allowed us to step in quickly to support each other. At times, one of us would even build a feature on the spot to help the other.
Momentum quickly picked up from there. With the collaboration in full swing and the tools working seamlessly together, we saw an opportunity to share our work with others. Rick joined Moderne’s weekly Code Remix to talk about Error Prone Support and how Picnic is using Refaster internally [8]. Presenting together proved to be both effective and enjoyable, inspiring us to organize similar sessions. We created a workshop and began submitting proposals, eventually running sessions at JCON Europe, Devoxx Belgium, and J-Fall to demonstrate the tools in action. The enthusiastic response from participants was encouraging and motivated us to keep pushing forward.
Current State
Currently, the OpenRewrite annotation processor can convert 437 out of the 950+ rules that Picnic’s Error Prone Support provides into OpenRewrite recipes. These rules are already integrated into larger OpenRewrite recipe composites, to enable tasks like switching from TestNG to AssertJ, or applying best practices for AssertJ or Reactor. These generated recipes are then combined with imperative recipes that go beyond what’s possible with Refaster, such as chaining consecutive assertions [9] or adding new dependencies.
For example, consider a recipe that collapses multiple assertions on the same object into a single chained assertion. This type of transformation isn’t possible with Refaster alone, but users of Picnic’s Error Prone Support can now leverage Moderne’s OpenRewrite to achieve these additional benefits. If you want to try it out on your own codebase, go to the AssertJ best practices page [10].
Before
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class MyTest {
void test() {
ListlistA = Arrays.asList("a", "b", "c");
assertThat(listA).isNotNull();
assertThat(listA).hasSize(3);
assertThat(listA).containsExactly("a", "b", "c");
}
}
After
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class MyTest {
void test() {
ListlistA = Arrays.asList("a", "b", "c");
assertThat(listA)
.isNotNull()
.hasSize(3)
.containsExactly("a", "b", "c");
}
}
Another benefit is that folks can now use Moderne tooling to do an initial clean sweep across an organization. By selecting the desired recipes and repositories, teams can quickly create pull requests across all of them before enforcing those standards going forward. This removes the hassle of getting started and helps drive adoption. Additionally, because OpenRewrite is natively integrated into IntelliJ Ultimate, it provides another easy way to run the Picnic rules on projects without changing build files at all.
What’s Next?
Looking ahead, there’s still more we want to cover. Refaster has support for both nice and niche use cases, which have not yet been ported to the OpenRewrite annotation processor. Examples include the @AlsoNegation annotation that generates a second set of rules that matches the inverse of boolean expressions. Generics are not supported at the time of writing, but would unlock a large additional set of rules to be converted into recipes. Beyond that, features like the @Placeholder annotation–though less common–present a more challenging opportunity for future integrations. With so much potential, there’s always something to tinker with on a rainy afternoon or quiet weekend to further enhance these tools.
From the Error Prone Support side of things there are also improvements to be made that would make the collaboration and integration even better. Currently, Error Prone Supporte is using a manual release process with an ad-hoc cadence, whereas OpenRewrite releases every two weeks. A more predictable release schedule would ensure rules are picked up and made available to users sooner, encouraging rules to be added to Error Prone Support first.
Another potential improvement is to support rules that will only match on specific Java versions. As an example, list.get(0) can be replaced with list.getFirst() on Java 21+, but Error Prone Support for now uses Java 17. The inverse is also possible: some methods on CharSequence are only available on Java 15+, whereas we want to continue to support Java 8.
Finally, we’re considering to re-use Error Prone Support’s test suite and documentation in OpenRewrite.
Conclusion
When you open source a project, you never know where it might lead. In our case, it sparked a very productive collaboration that benefited everyone involved and deepened our understanding of the underlying technologies. By joining forces we’ve enabled our respective users to tackle new use cases that would have been challenging to achieve individually. This collaboration has also expanded the reach of Picnic’s Error Prone Support rules–extending from our direct users to some of the largest companies in the world through Moderne. Not too bad for an idea that started with a friendly chat in the back of a cab in Barcelona.
Special thanks to Knut Wannheden and Stephan Schroevers for their invaluable contributions to making this integration possible.
Links
[2]: https://blog.picnic.nl/developing-a-java-plugin-that-automatically-fixes-code-a712a07a7b4e
[3]: https://docs.openrewrite.org
[4]: https://blog.picnic.nl/picnic-open-sources-error-prone-support-b23f9a7208b6
[5]: https://github.com/openrewrite/rewrite-templating
[6]: https://github.com/PicnicSupermarket/error-prone-support/pull/925
[7]: https://github.com/spring-projects/spring-integration-aws/pull/251
[8]: https://www.youtube.com/watch?v=DUc53vuJQ7Q
[9]: https://docs.openrewrite.org/recipes/java/testing/assertj/collapseconsecutiveassertthatstatements
[10]: https://docs.openrewrite.org/recipes/java/testing/assertj/assertj-best-practices
Refaster & OpenRewrite: Better Together was originally published in Picnic Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.