[00:00.000 --> 00:09.920] Okay, continuing with the immigration topic. [00:09.920 --> 00:14.560] There is a relatively new tool called Open Rewrite, and you're going to hear all about [00:14.560 --> 00:15.560] it from Tim. [00:15.560 --> 00:16.560] Hi. [00:16.560 --> 00:22.520] It's been mentioned a few times before, so I hope I can live up to the hype. [00:22.520 --> 00:27.560] My name's Tim Tobake, and I'm a staff software engineer at Moderna. [00:27.560 --> 00:33.360] I recently started just the start of this month, and before that I was a migration engineer [00:33.360 --> 00:36.840] as a consultant for five years. [00:36.840 --> 00:44.120] So what that means is I would walk into organizations, familiarize myself with all the old technologies [00:44.120 --> 00:50.560] that they were still using, and then hack away at lifting all those services up to the [00:50.560 --> 00:54.120] latest versions of Java and Sprint. [00:54.120 --> 01:00.720] I would frequently find versions that up to five to ten-year-old versions of Java or [01:00.720 --> 01:06.560] JUnit or Sprint, which is not ideal from either a securities perspective or even a developer [01:06.560 --> 01:09.640] experience point of view. [01:09.640 --> 01:14.680] Initially I would migrate these services by hand, or gradually introduce more and more [01:14.680 --> 01:17.920] force automations. [01:17.920 --> 01:26.400] But then at the end of the early last year, I discovered Open Rewrite, and Open Rewrite [01:26.400 --> 01:31.080] is a tool that promises to make light work of all such migrations. [01:31.080 --> 01:35.640] I got so excited by this technology that I started to contribute and even present about [01:35.640 --> 01:43.760] this on conferences, and then eventually quit my job to work on Open Rewrite full-time. [01:43.760 --> 01:48.040] So after a nice sabbatical, that brings us here today. [01:48.040 --> 01:51.200] Perhaps you've faced some of the same challenges that I did. [01:51.200 --> 01:55.760] At a conference like this, you'll hear all about new framework and language features. [01:55.760 --> 02:02.320] Yeah, back at work, you've stuck to using Java 8 and JUnit 4. [02:02.320 --> 02:07.880] And migrating all of that by hand can seem daunting, if it ever gets priority. [02:07.880 --> 02:12.880] I want to show you how easy it can be to perform major migrations. [02:12.880 --> 02:17.800] That way you too can adopt all the latest language and framework features. [02:17.800 --> 02:23.200] And it can be fun to adopt new language features such as records and text blocks. [02:23.200 --> 02:28.360] But you don't want to adopt these features manually, you're only on a single project. [02:28.360 --> 02:32.680] Instead we will look into automations and make all projects feel like new again, so [02:32.680 --> 02:38.640] you can benefit from JGM, language and framework improvements. [02:38.640 --> 02:43.840] Here's a very brief overview of the types of migrations I'll be talking about. [02:43.840 --> 02:48.440] Likely you already performed some of these migrations in the past, and other migrations [02:48.440 --> 02:51.080] are always just around the corner. [02:51.080 --> 02:55.120] If you look back over time, there's a near constant stream of worthwhile improvements [02:55.120 --> 02:56.760] to pick up. [02:56.760 --> 03:01.080] And I like the challenge, I still get excited whenever a new version comes out. [03:01.080 --> 03:06.000] I just don't like the repetitive elements that come with upgrading. [03:06.000 --> 03:11.120] And if you try to keep up by hand, you will hardly get anything else done, especially [03:11.120 --> 03:17.720] as microservices these days mean you're not just upgrading once, but dozens of time. [03:17.720 --> 03:25.480] Automation may then be the only option, especially for companies using thousands of services. [03:25.480 --> 03:30.920] Through Open Rewrite you can now migrate between versions of Java and Spring with a simple [03:30.920 --> 03:31.920] command. [03:31.920 --> 03:38.280] You can even migrate between frameworks, such as from JUnit to AssertJ, and from Java.e [03:38.280 --> 03:39.280] to Spring. [03:39.280 --> 03:45.480] In this talk, I'll tell you all about Open Rewrite, how it came about, how it works, [03:45.480 --> 03:46.880] and what you can do with it. [03:46.880 --> 03:52.280] And finally, we'll briefly look at who is developing these recipes and how to apply [03:52.280 --> 03:56.680] them to open source projects. [03:56.680 --> 04:01.840] Open Rewrite was developed in Netflix, initially to aid in the migration of an internal logging [04:01.840 --> 04:04.880] framework to AssertJ. [04:04.880 --> 04:09.320] You can probably imagine that any logging framework is going to be pervasive throughout [04:09.320 --> 04:17.800] an organization, so even consider migrating using a perfectly accurate automation. [04:17.800 --> 04:24.240] So they, especially when usage is spread across hundreds of services, so they develop a parser [04:24.240 --> 04:30.600] to accurately read Java and turn the source code into a lossless semantic tree. [04:30.600 --> 04:35.520] This model can then be modified to replace the old learning statements with calls to [04:35.520 --> 04:38.040] AssertJ. [04:38.040 --> 04:43.200] Next the migrated model is running out as close as possible to the original source code. [04:43.200 --> 04:49.080] That way the applied changes are minimal, leaving the surrounding code untouched. [04:49.080 --> 04:54.560] Later, the same developers moved on to work on Spinnigar, and while trying to onboard [04:54.560 --> 04:59.680] teams and organizations there, they found that teams often struggled with the same outdated [04:59.680 --> 05:02.720] languages and framework. [05:02.720 --> 05:14.160] To help teams adopt the latest versions, to help teams adopt the latest versions, they [05:14.160 --> 05:20.280] applied a different set of migration recipes, through the same lossless semantic tree parser. [05:20.280 --> 05:26.560] Let me just get this one on. [05:26.560 --> 05:31.840] This allowed them to quickly reduce this technical depth and bring teams from Spring [05:31.840 --> 05:37.560] Book 1 to Spring Book 2 and from JUnit 4 to JUnit 5. [05:37.560 --> 05:42.320] The project has since been open sourced, with the company behind it committed to making [05:42.320 --> 05:48.400] all recipes available on the Apache license for open source software. [05:48.400 --> 05:53.680] The initial focus for open rewind is on the Java virtual machine languages and surrounding [05:53.680 --> 05:55.680] technologies. [05:55.680 --> 06:01.640] There are parsers, for instance, for Java, Groovy, Dunl and XML, and these in turn unlock [06:01.640 --> 06:10.200] support for builders such as Maven and Gradle, and libraries such as JUnit, AssertJ and Guava. [06:10.200 --> 06:15.560] Ultimately refactoring entire frameworks and platforms is supported, with recipes available [06:15.560 --> 06:22.480] for application frames such as Micronaut, Barkers and Spring. [06:22.480 --> 06:28.840] Open Rewrite is not the only parser capable of understanding and manipulating Java. [06:28.840 --> 06:33.360] However, three features set Open Rewrite apart from the competition. [06:33.360 --> 06:37.040] The first is to focus on exact type attribution. [06:37.040 --> 06:42.160] By having the exact type available on any tree element, we can be sure to only manipulate [06:42.160 --> 06:44.640] exact matches. [06:44.640 --> 06:51.240] The second characteristic that sets Open Rewrite apart is the form of preservation. [06:51.240 --> 06:55.320] The parser not only takes into account the functional code, but also the surrounding [06:55.320 --> 06:59.160] code style and implementation. [06:59.160 --> 07:05.000] This allows us to accurately reproduce your source code regardless of further changes. [07:05.000 --> 07:09.240] Changes made through Open Rewrite look just like our colleague worked on your code. [07:09.240 --> 07:15.600] And finally, the serialization format ensures you're able to query and refactor your code [07:15.600 --> 07:17.400] faster and at scale. [07:17.400 --> 07:23.520] Together, these features make Open Rewrite exceptionally good at safe code transformations, [07:23.520 --> 07:30.680] especially as the changes are minimally invasive and guaranteed to work in part of the due [07:30.680 --> 07:34.000] to the do no harm mentality. [07:34.000 --> 07:39.360] By manipulating the full lossless semantic tree, Open Rewrite can far exceed simple search [07:39.360 --> 07:43.520] and replace operations. [07:43.520 --> 07:48.160] With the full lossless semantic tree built, we need to instruct Open Rewrite what operations [07:48.160 --> 07:51.600] to apply and where in your code. [07:51.600 --> 07:56.920] Recipes are highly defined, such as a group of search and refactoring operations. [07:56.920 --> 08:01.880] Together they accomplish a higher level task, such as a framework migration. [08:01.880 --> 08:06.560] Recipes can consist of a single standalone operation, or be linked together with other [08:06.560 --> 08:09.400] recipes. [08:09.400 --> 08:13.720] Open Rewrite comes with a large collection of fine-grained recipes out of the box that [08:13.720 --> 08:17.080] can be combined for common migration steps. [08:17.080 --> 08:22.680] You can think of these as LEGO building blocks, ready to be applied with the proper parameters. [08:22.680 --> 08:27.880] There are hundreds of these building blocks to, for instance, change types, change methods, [08:27.880 --> 08:35.320] change arguments, manipulate properties, and alter dependencies of plugins. [08:35.320 --> 08:40.720] Full recipes are implemented as Java visitors that first match and then modify elements [08:40.720 --> 08:42.480] of the lossless semantic tree. [08:42.480 --> 08:47.960] There are plenty of examples available, but notice that you only need a dedicated Java [08:47.960 --> 08:52.760] visitor, but none of the existing recipes can only really achieve your goals. [08:52.760 --> 08:59.920] Typically, you can get very far, only configuring, combining, applying existing recipes through [08:59.920 --> 09:03.840] a YAML description form. [09:03.840 --> 09:09.920] Examples then group together these fine-grained recipes into more coarse-grained, application-specific [09:09.920 --> 09:10.920] recipes. [09:10.920 --> 09:16.800] There are modules, for example, for loading frameworks, testing frameworks, and application [09:16.800 --> 09:19.280] frameworks, such as Spring. [09:19.280 --> 09:26.440] Think of these as LEGO sets, with built plans for common migrations ready to be applied. [09:26.440 --> 09:31.320] In my opinion, the lossless semantic tree, combined with a large collection of fine-grained [09:31.320 --> 09:36.440] recipes, is what sets open-grained apart from other similar tools, such as error-prones [09:36.440 --> 09:37.440] and repositories. [09:37.440 --> 09:44.800] Now, I want to show you how migration recipes are configured in OpenRero. [09:44.800 --> 09:48.520] Let's briefly look at a migration from JUnit4 to JUnit5. [09:48.520 --> 09:53.920] I want you to imagine the steps of what we need after such a migration. [09:53.920 --> 09:59.360] You've likely applied some of those steps already in the past. [09:59.360 --> 10:03.040] Some others should have to update the test annotations. [10:03.040 --> 10:07.760] But you would also have to update the assertions, and sometimes the argument order would have [10:07.760 --> 10:14.040] to update all imports, and they have to update any test rules, and that's just getting started. [10:14.040 --> 10:19.240] Notice how each of these steps is reflected as a separate recipe in this YAML configuration [10:19.240 --> 10:20.960] form. [10:20.960 --> 10:26.520] Some refer to and prefer the NERC steps, such as the change-type recipe. [10:26.520 --> 10:31.320] Others are implemented as an imperative set, a dedicated Java visitor that changes the [10:31.320 --> 10:33.760] lossless semantic tree. [10:33.760 --> 10:38.920] All these steps combine to achieve a complete JUnit5 migration. [10:38.920 --> 10:41.760] This is a common pattern with OpenRero. [10:41.760 --> 10:47.760] Large migrations are broken up into small, reusable steps. [10:47.760 --> 10:51.000] When we run this recipe, we get predictable results. [10:51.000 --> 10:56.960] Our imports are replaced, as we would expect, and our Makito runner is replaced into using [10:56.960 --> 10:58.960] the extension. [10:58.960 --> 11:04.120] Life cycle annotations, such as that before, are correctly replaced. [11:04.120 --> 11:08.920] But interestingly, we can see how OpenRero shines through when it comes to comparing [11:08.920 --> 11:10.120] expected exceptions. [11:10.120 --> 11:16.600] Having the full power of a lossless semantic tree, combined with a Java visitor, allows [11:16.600 --> 11:19.160] us to adopt assert throws. [11:19.160 --> 11:25.880] Since these types of changes that would not be possible with a regular expression approach. [11:25.880 --> 11:28.080] Running migration recipes is fairly straightforward. [11:28.080 --> 11:32.480] First, you apply a built-in plugin for OpenRero. [11:32.480 --> 11:37.560] I've used Maven in my example, but Gradle works just as well. [11:37.560 --> 11:41.880] Then depending on the changes you want to make, you add a dependency on the respective [11:41.880 --> 11:42.880] OpenRero module. [11:42.880 --> 11:50.080] Lastly, you run the OpenRero plugin with the migration recipe that you want to execute. [11:50.080 --> 11:56.840] The command scene here will migrate an application from Spring Boot 1.5 to the latest Spring [11:56.840 --> 12:02.600] Boot 2.7 branch, and we're also working on a 3.0 migration. [12:02.600 --> 12:07.080] This migration works all the way back to Spring Boot 1.5. [12:07.080 --> 12:13.160] They will update dependencies, properties, and deprecations from any older versions, [12:13.160 --> 12:18.400] and it includes the JU5 migration we've seen before, as well as any Spring-specific test [12:18.400 --> 12:19.400] constructs. [12:19.400 --> 12:26.760] Now that we've seen how OpenRero works, let's have a look at what you can do with it. [12:26.760 --> 12:31.000] Obviously, but now we've seen it is well-suited to migrations. [12:31.000 --> 12:36.840] You've mostly seen migrations from one version to another, but you can also migrate from [12:36.840 --> 12:39.120] one framework to another. [12:39.120 --> 12:43.920] If you want to switch from large for J to as a large for J, you can, and the same thing [12:43.920 --> 12:52.160] if you want to switch between JUnit and AssertJ, and even larger migrations are in development. [12:52.160 --> 12:55.640] Another application is fixing static analysis findings. [12:55.640 --> 13:00.640] A large collection of checkstiles, sonar, and security findings are supported to allow [13:00.640 --> 13:04.200] you to reduce your technical debt in minutes. [13:04.200 --> 13:10.480] Finally, there's a whole class of recipes to enforce a code style, and rather than merely [13:10.480 --> 13:17.160] apply a formator, these style recipes go a step further to actually change your code. [13:17.160 --> 13:21.720] This ensures your code style is consistently from project to project. [13:21.720 --> 13:26.400] In addition to what's already available, it's fairly easy to add custom migration recipes [13:26.400 --> 13:30.600] specific to your project. [13:30.600 --> 13:34.160] Now that we've seen how it works and what you can do with it, let's briefly look at [13:34.160 --> 13:37.080] what is still to come. [13:37.080 --> 13:43.480] As you've seen, OpenRemo has dedicated parsers for multiple languages already, but we have [13:43.480 --> 13:46.160] some catching up to do still. [13:46.160 --> 13:52.280] We are working on a parser for both Java 18 and up and Kotlin, but note that you're perfectly [13:52.280 --> 13:58.800] able to run on Java 17, but you cannot yet migrate to some of the new language features. [13:58.800 --> 14:03.360] The interesting thing about Kotlin is going to be that the Java migration recipes that [14:03.360 --> 14:09.280] we have will also just work, even though the languages look very different. [14:09.280 --> 14:14.440] Another subject we're working on is data flow analysis, and this not only takes into account [14:14.440 --> 14:21.560] the individual code statements, but also how data flows throughout your application. [14:21.560 --> 14:29.840] This will allow recipes to, for instance, add immutability or dedicated security fixes. [14:29.840 --> 14:34.440] Another interesting development is the Spring Boot Migrator project from VMR. [14:34.440 --> 14:41.040] It builds upon OpenRemo to migrate projects towards Spring from other frameworks. [14:41.040 --> 14:45.920] It takes a slightly different, more interactive approach, which will be coming handy when [14:45.920 --> 14:49.880] it comes to the Spring Boot 3 migration. [14:49.880 --> 14:52.000] All these features are inactive development. [14:52.000 --> 14:55.880] It's not yet clear when you can use this in a production setting, but it's interesting [14:55.880 --> 14:59.800] developments nonetheless. [14:59.800 --> 15:04.280] There's a last subject I want to tell you a bit about the company behind OpenRemo. [15:04.280 --> 15:10.440] As I said before, Moderna has committed to making all recipes available open source. [15:10.440 --> 15:14.080] Our focus is on applying recipes at scale. [15:14.080 --> 15:18.600] Through Moderna, clients can discover code patterns across an entire organization and [15:18.600 --> 15:21.880] target these for transformation. [15:21.880 --> 15:25.920] And even if you're not a paying customer, you can still use the web interface to browse [15:25.920 --> 15:30.520] available recipes and even apply them to open source projects. [15:30.520 --> 15:34.800] This can be a great way to start contributing back to open source software. [15:34.800 --> 15:39.600] And if you find any of the migration steps are missing, OpenRemo itself is very accepting [15:39.600 --> 15:42.440] of new contributions. [15:42.440 --> 15:46.600] The community plays a large role in the development of new recipes. [15:46.600 --> 15:53.480] Now, as you could probably tell from my email address, we're not exactly a big company. [15:53.480 --> 15:57.240] But we're pretty well connected in the broader Java community. [15:57.240 --> 16:02.840] Through collaborations, other companies contribute migration recipes for their remotes. [16:02.840 --> 16:10.080] And this ensures their users are able to migrate easily and timely with new releases. [16:10.080 --> 16:15.880] And if you maintain or merely enjoy your particular library or framework, you can help other users [16:15.880 --> 16:20.480] by providing migration recipes. [16:20.480 --> 16:23.600] So with that, we are getting near the end of my presentation. [16:23.600 --> 16:29.160] Before I send you away, I want to recommend a few resources where you can learn more. [16:29.160 --> 16:33.920] There's extensive documentation available on OpenRemo. [16:33.920 --> 16:38.960] Development is all on GitHub with new suggestions typically picked up with surprising speed. [16:38.960 --> 16:45.360] And as you've all seen, it's quite easy to contribute minor migration steps. [16:45.360 --> 16:52.680] If you want to try some recipes quickly on OpenSource software, have a look at public.moderna.io. [16:52.680 --> 16:57.400] And if you have any questions, you can reach out on our public slide or via email. [16:57.400 --> 17:03.840] And finally, if you would like to play around with the months you've seen before, I've [17:03.840 --> 17:07.520] written a blog post to accompany this presentation. [17:07.520 --> 17:12.920] This blog post migrates an old Spring Path Clinic branch from Spring Boot 1.5 to Spring [17:12.920 --> 17:17.320] on Java 8 to Spring Boot 2.x on Java 17. [17:17.320 --> 17:23.040] That way you can play around with your commands and see the changes made every step. [17:23.040 --> 17:26.880] For your own projects, I recommend you start with the testing framework migrations. [17:26.880 --> 17:33.120] They're an easy way to gain confidence in the tool and see what it can do for your project. [17:33.120 --> 17:43.120] And with that, I'd like to thank you for your attention. [17:43.120 --> 17:47.320] What's also really great about Tim's story is that he was enthusiastic about a project. [17:47.320 --> 17:52.120] He started contributing to it and now he was offered a job when he's working at the company [17:52.120 --> 17:53.640] behind this project. [17:53.640 --> 18:01.200] It's really the textbook story of starting contributing and then getting paid to do that [18:01.200 --> 18:04.480] and joining the company behind the project. [18:04.480 --> 18:33.440] Well, thank you very much.