Okay, continuing with the immigration topic.
There is a relatively new tool called Open Rewrite, and you're going to hear all about
it from Tim.
Hi.
It's been mentioned a few times before, so I hope I can live up to the hype.
My name's Tim Tobake, and I'm a staff software engineer at Moderna.
I recently started just the start of this month, and before that I was a migration engineer
as a consultant for five years.
So what that means is I would walk into organizations, familiarize myself with all the old technologies
that they were still using, and then hack away at lifting all those services up to the
latest versions of Java and Sprint.
I would frequently find versions that up to five to ten-year-old versions of Java or
JUnit or Sprint, which is not ideal from either a securities perspective or even a developer
experience point of view.
Initially I would migrate these services by hand, or gradually introduce more and more
force automations.
But then at the end of the early last year, I discovered Open Rewrite, and Open Rewrite
is a tool that promises to make light work of all such migrations.
I got so excited by this technology that I started to contribute and even present about
this on conferences, and then eventually quit my job to work on Open Rewrite full-time.
So after a nice sabbatical, that brings us here today.
Perhaps you've faced some of the same challenges that I did.
At a conference like this, you'll hear all about new framework and language features.
Yeah, back at work, you've stuck to using Java 8 and JUnit 4.
And migrating all of that by hand can seem daunting, if it ever gets priority.
I want to show you how easy it can be to perform major migrations.
That way you too can adopt all the latest language and framework features.
And it can be fun to adopt new language features such as records and text blocks.
But you don't want to adopt these features manually, you're only on a single project.
Instead we will look into automations and make all projects feel like new again, so
you can benefit from JGM, language and framework improvements.
Here's a very brief overview of the types of migrations I'll be talking about.
Likely you already performed some of these migrations in the past, and other migrations
are always just around the corner.
If you look back over time, there's a near constant stream of worthwhile improvements
to pick up.
And I like the challenge, I still get excited whenever a new version comes out.
I just don't like the repetitive elements that come with upgrading.
And if you try to keep up by hand, you will hardly get anything else done, especially
as microservices these days mean you're not just upgrading once, but dozens of time.
Automation may then be the only option, especially for companies using thousands of services.
Through Open Rewrite you can now migrate between versions of Java and Spring with a simple
command.
You can even migrate between frameworks, such as from JUnit to AssertJ, and from Java.e
to Spring.
In this talk, I'll tell you all about Open Rewrite, how it came about, how it works,
and what you can do with it.
And finally, we'll briefly look at who is developing these recipes and how to apply
them to open source projects.
Open Rewrite was developed in Netflix, initially to aid in the migration of an internal logging
framework to AssertJ.
You can probably imagine that any logging framework is going to be pervasive throughout
an organization, so even consider migrating using a perfectly accurate automation.
So they, especially when usage is spread across hundreds of services, so they develop a parser
to accurately read Java and turn the source code into a lossless semantic tree.
This model can then be modified to replace the old learning statements with calls to
AssertJ.
Next the migrated model is running out as close as possible to the original source code.
That way the applied changes are minimal, leaving the surrounding code untouched.
Later, the same developers moved on to work on Spinnigar, and while trying to onboard
teams and organizations there, they found that teams often struggled with the same outdated
languages and framework.
To help teams adopt the latest versions, to help teams adopt the latest versions, they
applied a different set of migration recipes, through the same lossless semantic tree parser.
Let me just get this one on.
This allowed them to quickly reduce this technical depth and bring teams from Spring
Book 1 to Spring Book 2 and from JUnit 4 to JUnit 5.
The project has since been open sourced, with the company behind it committed to making
all recipes available on the Apache license for open source software.
The initial focus for open rewind is on the Java virtual machine languages and surrounding
technologies.
There are parsers, for instance, for Java, Groovy, Dunl and XML, and these in turn unlock
support for builders such as Maven and Gradle, and libraries such as JUnit, AssertJ and Guava.
Ultimately refactoring entire frameworks and platforms is supported, with recipes available
for application frames such as Micronaut, Barkers and Spring.
Open Rewrite is not the only parser capable of understanding and manipulating Java.
However, three features set Open Rewrite apart from the competition.
The first is to focus on exact type attribution.
By having the exact type available on any tree element, we can be sure to only manipulate
exact matches.
The second characteristic that sets Open Rewrite apart is the form of preservation.
The parser not only takes into account the functional code, but also the surrounding
code style and implementation.
This allows us to accurately reproduce your source code regardless of further changes.
Changes made through Open Rewrite look just like our colleague worked on your code.
And finally, the serialization format ensures you're able to query and refactor your code
faster and at scale.
Together, these features make Open Rewrite exceptionally good at safe code transformations,
especially as the changes are minimally invasive and guaranteed to work in part of the due
to the do no harm mentality.
By manipulating the full lossless semantic tree, Open Rewrite can far exceed simple search
and replace operations.
With the full lossless semantic tree built, we need to instruct Open Rewrite what operations
to apply and where in your code.
Recipes are highly defined, such as a group of search and refactoring operations.
Together they accomplish a higher level task, such as a framework migration.
Recipes can consist of a single standalone operation, or be linked together with other
recipes.
Open Rewrite comes with a large collection of fine-grained recipes out of the box that
can be combined for common migration steps.
You can think of these as LEGO building blocks, ready to be applied with the proper parameters.
There are hundreds of these building blocks to, for instance, change types, change methods,
change arguments, manipulate properties, and alter dependencies of plugins.
Full recipes are implemented as Java visitors that first match and then modify elements
of the lossless semantic tree.
There are plenty of examples available, but notice that you only need a dedicated Java
visitor, but none of the existing recipes can only really achieve your goals.
Typically, you can get very far, only configuring, combining, applying existing recipes through
a YAML description form.
Examples then group together these fine-grained recipes into more coarse-grained, application-specific
recipes.
There are modules, for example, for loading frameworks, testing frameworks, and application
frameworks, such as Spring.
Think of these as LEGO sets, with built plans for common migrations ready to be applied.
In my opinion, the lossless semantic tree, combined with a large collection of fine-grained
recipes, is what sets open-grained apart from other similar tools, such as error-prones
and repositories.
Now, I want to show you how migration recipes are configured in OpenRero.
Let's briefly look at a migration from JUnit4 to JUnit5.
I want you to imagine the steps of what we need after such a migration.
You've likely applied some of those steps already in the past.
Some others should have to update the test annotations.
But you would also have to update the assertions, and sometimes the argument order would have
to update all imports, and they have to update any test rules, and that's just getting started.
Notice how each of these steps is reflected as a separate recipe in this YAML configuration
form.
Some refer to and prefer the NERC steps, such as the change-type recipe.
Others are implemented as an imperative set, a dedicated Java visitor that changes the
lossless semantic tree.
All these steps combine to achieve a complete JUnit5 migration.
This is a common pattern with OpenRero.
Large migrations are broken up into small, reusable steps.
When we run this recipe, we get predictable results.
Our imports are replaced, as we would expect, and our Makito runner is replaced into using
the extension.
Life cycle annotations, such as that before, are correctly replaced.
But interestingly, we can see how OpenRero shines through when it comes to comparing
expected exceptions.
Having the full power of a lossless semantic tree, combined with a Java visitor, allows
us to adopt assert throws.
Since these types of changes that would not be possible with a regular expression approach.
Running migration recipes is fairly straightforward.
First, you apply a built-in plugin for OpenRero.
I've used Maven in my example, but Gradle works just as well.
Then depending on the changes you want to make, you add a dependency on the respective
OpenRero module.
Lastly, you run the OpenRero plugin with the migration recipe that you want to execute.
The command scene here will migrate an application from Spring Boot 1.5 to the latest Spring
Boot 2.7 branch, and we're also working on a 3.0 migration.
This migration works all the way back to Spring Boot 1.5.
They will update dependencies, properties, and deprecations from any older versions,
and it includes the JU5 migration we've seen before, as well as any Spring-specific test
constructs.
Now that we've seen how OpenRero works, let's have a look at what you can do with it.
Obviously, but now we've seen it is well-suited to migrations.
You've mostly seen migrations from one version to another, but you can also migrate from
one framework to another.
If you want to switch from large for J to as a large for J, you can, and the same thing
if you want to switch between JUnit and AssertJ, and even larger migrations are in development.
Another application is fixing static analysis findings.
A large collection of checkstiles, sonar, and security findings are supported to allow
you to reduce your technical debt in minutes.
Finally, there's a whole class of recipes to enforce a code style, and rather than merely
apply a formator, these style recipes go a step further to actually change your code.
This ensures your code style is consistently from project to project.
In addition to what's already available, it's fairly easy to add custom migration recipes
specific to your project.
Now that we've seen how it works and what you can do with it, let's briefly look at
what is still to come.
As you've seen, OpenRemo has dedicated parsers for multiple languages already, but we have
some catching up to do still.
We are working on a parser for both Java 18 and up and Kotlin, but note that you're perfectly
able to run on Java 17, but you cannot yet migrate to some of the new language features.
The interesting thing about Kotlin is going to be that the Java migration recipes that
we have will also just work, even though the languages look very different.
Another subject we're working on is data flow analysis, and this not only takes into account
the individual code statements, but also how data flows throughout your application.
This will allow recipes to, for instance, add immutability or dedicated security fixes.
Another interesting development is the Spring Boot Migrator project from VMR.
It builds upon OpenRemo to migrate projects towards Spring from other frameworks.
It takes a slightly different, more interactive approach, which will be coming handy when
it comes to the Spring Boot 3 migration.
All these features are inactive development.
It's not yet clear when you can use this in a production setting, but it's interesting
developments nonetheless.
There's a last subject I want to tell you a bit about the company behind OpenRemo.
As I said before, Moderna has committed to making all recipes available open source.
Our focus is on applying recipes at scale.
Through Moderna, clients can discover code patterns across an entire organization and
target these for transformation.
And even if you're not a paying customer, you can still use the web interface to browse
available recipes and even apply them to open source projects.
This can be a great way to start contributing back to open source software.
And if you find any of the migration steps are missing, OpenRemo itself is very accepting
of new contributions.
The community plays a large role in the development of new recipes.
Now, as you could probably tell from my email address, we're not exactly a big company.
But we're pretty well connected in the broader Java community.
Through collaborations, other companies contribute migration recipes for their remotes.
And this ensures their users are able to migrate easily and timely with new releases.
And if you maintain or merely enjoy your particular library or framework, you can help other users
by providing migration recipes.
So with that, we are getting near the end of my presentation.
Before I send you away, I want to recommend a few resources where you can learn more.
There's extensive documentation available on OpenRemo.
Development is all on GitHub with new suggestions typically picked up with surprising speed.
And as you've all seen, it's quite easy to contribute minor migration steps.
If you want to try some recipes quickly on OpenSource software, have a look at public.moderna.io.
And if you have any questions, you can reach out on our public slide or via email.
And finally, if you would like to play around with the months you've seen before, I've
written a blog post to accompany this presentation.
This blog post migrates an old Spring Path Clinic branch from Spring Boot 1.5 to Spring
on Java 8 to Spring Boot 2.x on Java 17.
That way you can play around with your commands and see the changes made every step.
For your own projects, I recommend you start with the testing framework migrations.
They're an easy way to gain confidence in the tool and see what it can do for your project.
And with that, I'd like to thank you for your attention.
What's also really great about Tim's story is that he was enthusiastic about a project.
He started contributing to it and now he was offered a job when he's working at the company
behind this project.
It's really the textbook story of starting contributing and then getting paid to do that
and joining the company behind the project.
Well, thank you very much.