A few days ago, João Malés wrote an extensive article on migrating from .NET Framework to .NET Core. Back then, we had a considerable challenge: more than 60 projects with many third party dependencies, no previous experience on similar work, and the additional constraint of migrating in the same codebase that was going out to developers.
In essence, creating a new Service Studio while bringing value to our customers and having weekly releases in the existing Service Studio. Easy, right?
Now, the challenge was different: we had well-documented (and publicly shared :) ) knowledge from that experience, and more relevantly, we only needed to migrate a part of the codebase from Integration Studio (another OutSystems IDE).
Still, this was an elephant task. To divide it into bite-size pieces, we decided to experiment with other tools, such as Amazon Web Services (AWS) Porting Assistant and CodeTrack.
But first things first.
Do You Need to Migrate Everything?
As I’ve mentioned, migrating Service Studio to .NET Core was complex due to all the constraints. Let me highlight two:
- We had to migrate (almost) everything.
- Duplicating code was not an option because we had more than 150 engineers working in the same codebase. We couldn’t have everyone duplicating the same developments on both solutions all the time.
So, ask yourself:
- Do you need to migrate everything?
- Can you duplicate the code, or do you need to change it continuously?
- Can you halt operations to perform this work?
In this case, we only needed to migrate a part of the code, and duplication was an option given the smaller scope. This time around, we took a different approach.
A New Project
Instead of changing the car wheels while in motion, we opted to create a new, separate solution, from scratch, in .NET Core and then move the required code to the new solution. “What if someone needs to change the code you are duplicating?” you may ask. Well, you either create a test that fails if someone changes that code, or you put alarmistics “on build” (using, for instance, Directory.build.targets — I’ll write about it in my next post, stay tuned).
But before we copied the code, we needed to understand the critical path of the code we wanted to migrate. We knew the entry point but needed to understand all the paths the entry point went through to provide the final result. To do that, we experimented with a tool called CodeTrack. CodeTrack is a free .NET profiler and execution analyzer. There are other tools out there, but we went with this one.
It’s pretty simple: set the executable you want to profile, the optional arguments, it will run the app, and that’s it. For us, it was perfect! The result is a file with the profile result. Click Analyze, and, apart from other goodies, you’ll get exactly what we did: a code trace.
In this example, you can see that it started with one thread in “Main,” then called a Start method from the Car class, then a Console Writeline. With this, we had everything we needed for the new .NET Core solution. At this point, we were still researching, so our next step was to estimate how expensive it would be to migrate to .NET Core.
AWS Porting Assistant
In the article, João Malés referred to Microsoft’s Portability Analyzer:
“This tool focuses on analyzing your code and giving you a thorough report afterward regarding the compatibility between your current framework and the selected target frameworks. However, while the tool can give you a great starting point, don’t trust the results blindly. There are some false negatives, mostly regarding third-party libraries.”
With this in mind, we decided to try out another tool. In between the work we did in Service Studio and this challenge, AWS launched the AWS Porting Assistant for .NET (July 2020). You can read the public announcement here. Recently, they even open-sourced a part of it.
Like everything else out from AWS, the UI is as simple as it gets. The setup process is not seamless, though: you’ll need an AWS account with specific permissions. But after that, it’s effortless. Just provide the solution you want to assess, and it will give you all the goodies one can expect.
Let's take a look at the relevant information:
- Incompatible packages: if your project refers to other libraries, be it through direct assembly reference or NuGet package, it will highlight them here. Heads-up: if the tool does not have access to the sources (like the source code of the package), it will consider it incompatible. You’ll have to go after those one by one.
- Incompatible APIs: here, you’ll find all the API calls in your code that do not exist in .NET Core. I’ll provide more details on this below.
- Portability Score: quoting AWS, “This score is an estimation of the eﬀort required to port the application to .NET Core, based on the number of incompatible APIs it uses.”
Now, if we dig in a bit deeper, we’ll get to the solution assessment, which contains a more detailed explanation of each of the projects that comprise your solution.
Here, you’ll have the Projects and all the same information from the overall solution (Incompatible packages, Incompatible APIs and Portability Score), plus a few more interesting tabs. The Project References is a cool graph with the dependencies but, to be honest, we didn’t benefit much from it this time. However, it was useful when we migrated Service Studio since it was a big solution with many dependencies.
The NuGet packages tab is just what I mentioned before but in detail. Whenever the tool does not have access or know the package, it will mark it as incompatible and you’ll have to go to the source to analyze it.
APIs and Source Files, on the other hand, are little treasure troves. APIs will highlight what is not available in .NET Core. For some of them, the tool can give you a proper replacement suggestion. For others, like Windows Forms or WCF… well, you’ll have to rewrite your application. Windows Forms is highly dependent on Windows APIs, so there’s no way around it.
The Source Files tab was the coolest feature. It will guide you on needed changes to each file and recommend a ‘fashionable’ replacement. Here’s an example:
After you’ve analyzed everything, you can either export the report or even port the projects using the tool. You can always return to the analyzer later, as it saves the results. If you use the tool to do the port, you can do it “in place” or copy it to a new location:
In the end, your solution most likely won’t compile because of the non-compatible packages and unreplaceable APIs (for instance, Windows Forms). You’ll have to do the manual work.
In our case, as we only wanted to port a part of the complete solution, we got lucky: the hard part — Windows Forms and WCF — were left out and most of the manual work was actually quite simple.
Choosing the Right Tools
Whenever I do a technical presentation about different software options, I always get hit by the question: which one is the best? As with everything in engineering, it depends. What’s most important? Cost? Speed? The developer’s learning curve?
The same principle applies here: is AWS Portability Analyzer better than Microsoft’s? Well, maybe. Or maybe you don’t have an AWS Account, nor you understand how it works, and you have Microsoft expertise in the house. Or perhaps you value UI simplicity. It depends. From my personal experience, I enjoyed the tool. The replacement suggestions are very useful, particularly if you’re on a research task.
As for porting to .NET Core, although this challenge was simpler than the one João Malés described, there’s no way around the fact that you’re moving from a big fat framework (.NET Framework) tightly coupled to Microsoft APIs to a stripped-down, cross-platform version (.NET Core). Unless you’re really lucky, you will have plenty of manual work ahead of you, and what’s more concerning, surprises. These tools result in many false negatives (even AWS Portability Analyzer failed to alert on some non-compliant calls, like ConfigurationManager.AppSettings). Properly account for them if you don’t have prior experience migrating from one framework to another.
Disclaimer: software evolves and these AWS Portability Analyzer features may no longer be available (or be different) when you read this post, particularly the UI. Nevertheless, the concepts and takeaways will likely remain valid.