Here’s the back story: senior development manager needs to move a legacy desktop application to the web. Our company gives him a proposal for a migration. The C-suite asks him “should we do this?”
And he votes no.
Here’s what his objections were and what actually happened (spoiler: we did the project). I learned a lot from his thinking and maybe you will too. Buckle up, this is going to be long. I’ll try not to make it stultifyingly dull.
This company–let’s just call them SuperDuperSoftware or SDS for short (I wish I could use the real name but we are still waiting on the suits to ok it from their end–this could take years)–is a very well established player in a health care-related vertical. Like a lot of market leaders, they got in early with a great solution, iterated on their solution, and recently found themselves attacked by new market entrants with web-native SaaS offerings. SDS has a large, rich desktop app which they deployed as a hybrid cloud offering using virtualization. It’s a bit of a kludge, but it works. However, the UX screams “1990s!!” and the competition was starting to hurt.
The other problem was change management. A desktop client could take days or weeks to release an update; a web application can get updated almost instantly.
Rewrite or migrate?
It’s not that SDS didn’t know how to write a native web app; they had lots of experience using Vue and React. They knew how they wanted to build the app so it would be stateless. But they also knew that would take at least 18 months, and probably two full years. And naturally over the course of two years, requirements are likely to change as market pressures shift and the competition brings out new features and capabilities. Freeze the existing app while hustling to rewrite it with the shifting sands of requirements? Or keep one team on the desktop app trying to achieve some kind of feature parity and another team rushing the rewrite out the door?
Either option would require a “pig in a python” surge in staffing that would be temporary. Leading to a third question of hiring and then laying off the extra staff or turning to more expensive contract help. Neither option was particularly appealing.
Migration, on the other hand, had its own set of issues. SDS had three they were concerned about. Let’s review each separately.
Issue 1: Is automated source code translation any good?
When I first met DeeDee Walsh (Twitter: @ddskier) at her new office, both of us had worked in developer tools for, well, longer than I want to state publicly. DeeDee and I knew each other from years back at Microsoft, so I knew she was the real deal. Still, when she first told me about Mobilize.Net and their approach to automated source code translation, my immediate reaction was, “yeah, that’s been tried for years and hasn’t amounted to doodly squat.”
So I can’t really blame my customer at SDS for doubting automated source code translation, because if you actually look at the resulting code from virtually every tool, it’s not pretty. The most basic ones just try to translate the syntactic elements like an early version of Google translate doing Mandarin to English. The resulting code is largely useless beyond the simplest of programs. Others create a sort of Frankencode result that’s neither the source nor the target language but a sort of mashup of each.
Still others require a proprietary runtime library, which can speed up the migration process but bakes in a dependency on a third party forever. In the past, we’ve seen developers left high and dry when that third party packed up and left town, taking the sources with them.
Ok, so code quality was important.
Issue 2: Network latency and stateful web apps
If the ideal web application is stateless, then desktop apps are the opposite. Would that introduce unacceptable latency in the web application post migration?
Before we get too far down in the weeds, let’s make sure we’re all on the same page about what “stateful” and “stateless” even mean.
Desktop apps are stateful
When I’m not writing blog posts and the weather isn’t terrible, I like to take photographs. Here’s one of my dog Hans back when he was a puppy (RIP big guy):
Regardless of how amazingly photogenic puppy Hans was, I still had to do some cropping and exposure touch up in Photoshop. And PS is the very definition of a stateful desktop app. Once I open the photo for editing, every edit I make is kept as state in the application’s memory. The objects open on the screen (like tool palettes) and their position are all part of the application state. When I save the file, that state (at least the editing part) is saved along with the file and can be retrieved later. If autosave is not enabled, and the application crashes (not exactly unknown with PS), the state is lost.
Web apps should be stateless, or so they tell me
What’s a web app, anyway?
In its simplest form, a web client (HTML et al on the browser) talks to a web server (running some kind of application code) which in turn talks to a database.
A lot (most?) web applications are of the “forms over data” model, where the client is a UI for CRUD (Create, Replace, Update, Delete) operations on a database. Imagine the hypothetical scenario of a United States Senator buying airline tickets to Cancun in the dead of winter. That Expedia app is creating a record in a sales database, updating a record in a database of available seats, perhaps deleting a record of a prior reservation. And so on. The app might fetch data on flights, prices, seat availability, and options for speedy return flights. Just in case.
Let’s look at the state of the state, so to speak. While the web app is being used, it’s seen by the web server as a session. It builds a model of the session in memory, keeping track of whatever information is entered by the user in the browser. At some point (based on rules) the web server will update information in the database with the state that it has been tracking. For example, say you are shopping on Amazon.com. You browse items like sunglasses, beach toys, and fake mustaches. Some things get put in your shopping cart, which might just be a structure in memory on the server. When you complete the purchase of those items, the database gets updated with orders to be filled, adjustments to inventory, and so on.
That’s an example of a stateful web application.
What about stateless?
Remember the “what color is the dress?” debate on the Internet a few years ago? Some people saw black and blue, some people saw white and gold. Within a week of the image showing up on Tumblr, over 10 million tweets about the dress had been created. And we say people don’t care about the important stuff anymore.
That controversy is nothing compared to asking software developers what a stateless application looks like. Trust me. Want to have fun? Just throw some popcorn in the microwave and then google “stateless web app.” Read enough and you’ll perhaps reach the same conclusion that I did: there’s no consensus on what “stateless” means, let alone what a “stateless web application” is.
Nevertheless, the description that seems most agreeable to me includes these elements:
Session state is not persistent on the server. When the session ends, the state is flushed.
Persistent state (preferences, history, and such like) can be retrieved from a database using either login credentials (like a banking app) or cookies/tokens for something less security-focused.
Is WebMAP stateless?
In a word, no.
But there’s a very good reason.
WebMAP is designed to expedite and simplify the goal of moving large, complex desktop application source code to a web native architecture using ASP.NET Core (server side) and Angular (client side). By “desktop apps” I mean VB6, PowerBuilder, and C# or VB.NET using Windows Forms and the .NET Framework. Those platforms are all “code behind” designs. MVC/MVVM really didn’t get going until WPF, which we currently don’t deal with. With a code behind pattern, business logic is attached to events on form objects. The view and the model are joined at the hip. The code is easy to write, easy to maintain, and easy to read. That’s why it’s so popular.
It also has huge drawbacks, like the very real challenges involved in moving a UI to a different form factor or look & feel. But that’s another story.
Those code behind apps are the very definition of stateful. Single user, single session: there’s zero reason for them to be stateless.
The WebMAP promise is one of speed and efficiency modernizing legacy apps. WebMAP moves the user interface code from WinForms designer files to Angular with Kendo (by default). The business logic, rules, classes, and objects are all cleanly replatformed from .NET Framework to ASP.NET Core as a well-architected web server. A lot of voodoo takes place in the background to make sure the app performs well while keeping the source code clean and familiar.
But it’s not magic. And the UI has to make round trips to the server for processing changes. That means latency could be an issue if the server response is slow or if the network is the bottleneck. It doesn’t, however, necessarily mean that a stateful web app will be a poor performer, or even that it will perform noticeably worse than a stateless web app. In fact, WebMAP apps have been deployed by numerous organizations who have reported excellent performance. Bottlenecks on the server side due to volume can be addressed with clusters and tools like Redis. Bottlenecks due to network issues will affect every browser session, regardless of architecture. It’s just that stateless app will make fewer server requests than a stateful one will.
Ok, so performance was important.
Issue 3: The process is out of their control
The remaining concern this particular development manager expressed to me was that our migration process was a “black box” where they hand off the code to us and six months later we give them a completed application. (Let me clarify: they chose to have us perform a complete “functionally equivalent” migration for them; many customers choose to license our tools and do the projects themselves.)
To be honest, it’s not an unreasonable concern. These decisions–rewrite, migrate, or do nothing–are consequential. For ISVs like SDS, a modernization decision can be existential; get it wrong and you might be out of business in a year or two, wiped out by competition or depleted of resources as a failing rewrite drags on (like this blog post).
The Mobilize.Net migration process
Honestly, we do have a process, and it’s not like we keep it secret. This customer had a valid concern, however, which I’ll address in a minute. First, though, let me do a quick run-through of how we go about migrating software. For companies that want to do their own migrations, the process is virtually identical.
The first step is a comprehensive analysis and plan. We call this a Migration Blueprint and most customers choose this optional step. We know from experience that normally 90+ percent of source code will migrate correctly to the target platform (older tools having more coverage than newer ones); the analysis at this stage is aimed at identifying the last mile work: code that has not yet been mapped in the tool for automation or else has no target corollary–these latter cases require some new engineering. For example, desktop apps can use an attached printer and Crystal Reports; a web app might use SQL Server Reporting Services (SSRS) and a PDF generation tool. Preferences previously stored as Windows registry values might get moved to a configuration file. Local file access might be switched to an “upload file” function. This sort of thing.
Writing new code using Agile methodology is an iterative process of collecting user stories as specs/requirements and developing the appropriate functionality as a series of sprints. With the best of intentions these projects more often than not (based on numerous studies) fail to meet key objectives like schedule, overall cost, and satisfactory functionality (see an actual case study here).
Migration is, in some ways, like the antithesis of Agile; the existing application becomes a perfect and complete specification for the migrated application. We strongly recommend not adding features or changing the capabilities of the app during the migration process. This allows the legacy application to be a verification tool for the migrated app: if a test passes the old app, the same test should pass on the new app. Thus part of the planning and project kickoff should include getting buy-off from stakeholders that a like-for-like modernization will be the first step; additional features, refactoring, and other changes can come once the new system is certified and put in production.
The next step in the migration process is to examine all the impedance mismatches and unmapped properties, methods, and events (PMEs) in the source code in order to increase the automation rate for migration.
By definition, there’s no way to automate migration most impedance mismatches to the new platform (we can provide guidance and support). PMEs are a different story. We classify PMEs on legacy platforms into three flavors: green, yellow, and red.
Green PMEs are already mapped in the tools. For example, the click event on a command button in VB6 maps perfectly to the OnClick() event in System.Windows.Forms.Button.
Yellow PMEs are those that could be mapped but haven’t yet. Why is that, one asks? Because they are either used almost never or because no customer has yet required the mapping. The surface area of all possible PMEs in .NET alone is enormous, and our research shows that 90 percent of all source code only uses about 10 percent of the Framework. For example, take that same .NET button class; there’s an event if the font changes. I’m pretty sure there was a use case for this event or Microsoft wouldn’t have bothered adding it, but it strikes me as something you wouldn’t see all that often in real world code. So that is a yellow PME.
Red PMEs are those that have no corollary in the target platform. For example,ON ERROR RESUME NEXT in VB6 has nothing comparable in .NET. In this case, the automated tool will flag that line of code as something you will have to fix.
All out tools are extensible; currently only the VBUC allows for non-Mobilize.Net customizations. (We’re working on a generic solution to allow customers to add their own custom mappings to all our tools; stay tuned.) The next step before running migration tools is going to be adding mappings (where it makes sense) to handle yellow PMEs. Every “yellow” that gets turned into a “green” is added to the main branch of the tool so the next release it’s available to everyone. Over time, coverage keeps increasing.
Managing and reporting the migration project
The “black box” comment our customer made–thinking back to his third and final concern about doing a project with Mobilize.Net–is entirely our fault. We clearly didn’t do enough in our initial discussions to explain how we partner with customers during a migration. The reality is we do a ton of stuff to achieve full transparency, including:
Weekly status meetings and reports
Live demos and videos
Scheduled code drops
Dashboards for CI/CD status, bug burn down charts, test case automation, and more
Technical meetings to review alternative engineering approaches to challenges (see impedance mismatch and red PMEs).
The final score
Ok, after all that, what’s the TL;DR? How did we do in the customer’s eyes? After all, he had voted “no” to using Mobilize.Net for their migration. But they did.
Was his no vote justified?
Issue 1: How was the automated code quality?
Good. The code is readable and maintainable. “You can look at it and still know what it does. So on that score I was pretty much proven wrong.”
We’ve always tried to default to better code quality when there’s any kind of choice to be made in the automation algorithms. Sometimes that means not converting code because there’s a chance that we will get it wrong. Sometimes it means relying on some C# helper files (sources are included, so no real dependency). Some parts are relatively trivial, like leaving all the object names intact and even passing comments along in their original locations.
Issue 2: Network latency
“The perceived performance of our application is now entirely down to the latency of the client, which is something I don’t control, and as an engineer, having something you don’t control is maddening.”
I get it. This is the nature of the architecture we create. Again, we’re making tradeoffs: decreased risk and time to market in exchange for ultimate theoretical architecture. What’s important to keep in mind with these kinds of modernization projects is that it is a step toward some nirvana-like design pattern (stateless back end, micro-services, serverless functions). Our guidance to customers is always the following:
Migrate the app like for like and put it in production
As you make updates, use that effort to refactor selectively towards your desired architecture
There’s no need to refactor code that is working perfectly: change for the sake of change might not be the best use of your resources.
I can’t disagree. Why build platform-specific applications that require either duplication on all relevant platforms or shut out part of your market? The web lets you be perfectly cross-platform in ways never really possible before. But the downside, as SDS pointed out, is that the performance of those web apps is dependent on network latency. A thickish client architecture can mitigate some of the pain, but at the end of the day if you find yourself somewhere with terrible broadband you are not going to enjoy your web experience. Architecture can only do so much. Bottlenecks control throughput, as the ports of Los Angeles and Long Beach can attest to.
Issue 3: The Mobilize.Net development process
“That part has been absolutely wonderful…they have the process down to an art form. It’s pretty much, ‘Here’s the problem. Here’s the solutions. Pick one.’ which is actually how I would like to delegate to my engineers.”
Naturally, we love hearing this kind of thing from our customers. Especially given the concerns he had originally held regarding our development process. The “black box” he thought he was getting. I have to ding us a little because we clearly didn’t do a good enough job up front so he would understand our process–and his visibility into it–adequately.
One final note on the process: assuming we would be late, he scheduled an update for our delivery date then had to change direction when we delivered exactly on time. I know our team took the date commitment very seriously and put in the effort needed to hit it.
Summing it all up
Checking in with my SDS development manager, he feels like of his three concerns, two were not justified (code quality and process) and one was (network latency). And with regard to the latter, they feel it’s something they can live with for now. So of the three good reasons he had for NOT doing business with Mobilize.Net, it really came down to basically none.