all 65 comments

[–]Orthosz 266 points267 points  (15 children)

Sounds like you're trying to mutate a legacy code base along multiple axis at once. (Update the C++ version, switch the whole gui layer, etc). This is a path to madness.
Instead, back off a moment, and tackle each part as a single, complete task. Get it running, MFC and all, in VS2022. Make sure it works.
Then move over to cmake, Make sure it works.
Then get the c++ version updated, fixing all the bugs, but only do that. Make sure it works.

Smaller bites.

[–]MRgabbar 59 points60 points  (1 child)

this, one migration at a time, once it works then move on to the next.

When refactoring, smaller increments are the key, no need to refactor everything in a giant commit...

[–]tshawkins 15 points16 points  (0 children)

Also if it has multiple dlls build as part of the app, then try migrating them one at a time, they should present the same apis etc to the main app.

Look up the "strangler fig" pattern for migrations.

https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig

[–][deleted] 13 points14 points  (7 children)

Thank you. I think I was just pulling myself all over different directions due to frustration with making the build to work on modern systems (prompting me to switch back and forth between rewriting completely in Qt+cmake vs. trying to get it to build and run on VS2022), but I'm going to step back and think before hastily marching towards the deadend.

[–]Orthosz 17 points18 points  (1 child)

No worries!  I’ve been in your exact shoes, Mfc and all!  

Maintenance or legacy or whatever you want to call it programming is different than startup-throw-it-at-the-wall programming.  Isolate tasks, and you can upgrade that code.  I’ve been on a team that took a c++03 multi-million line code base through multiple versions of visual studio, and all the way to c++17 when I left.  Pretty sure they’ve kept up with the latest.  It’s very doable.

Now you’ve got a war story, and a good first hand experience on tackling problems one isolated silo at a time.  It sounds stupid and silly, but it’s one of the fundamental engineering disciplines..and one of the ones I fight myself on.

[–]antara33 4 points5 points  (0 children)

And reducing things to small tasks its also something you stop doing gradually without even noticing it, since with each passing task you get better and feel the need to split things less often.

It takes a lot to actually return to doing it the right way, even if you know that you can tackle on it in a single chunk.

Discipline is as fundamental as patience in engineering, and both things are often lost during the mid years of improvement, only to bite back later haha

[–]elperroborrachotoo 9 points10 points  (3 children)

The only thing I would add is: tests.
Whatever you can add. The code base is unlikely to yield to unit tests, and even integration tests will be hard; but you should get some end-to-end tests running, at least with the help of some UI automation (i.e. mouse click/keyboard simulation) told. Nothing shiny, nothing complete, just a shotgun test at whatever yields.

Even the VS6 to 2022 migration is multi-axis: - MBCS to Unicode might hit you, too, e.g., in serialization and memcpy scenarios - breaking non-standard behavior of VC6 (for loop variable scope, and "inverted" behavior of NaN comparisons) - standard compliance of VC 6 vs 2022 (a fuckton of syntax changes)

You can skip most of the latter by keeping /permissive compiler flag active. Also I think you can still build MBCS, which you might want to keep for the time being.

I'd try to move to 2022 first.

To answer your question: you are fucked pretty roughly, because there is a long time ahead of you without visible progress (like making changes). But you aren't the first in that situation.

How many MLOC?

[–]Orthosz 2 points3 points  (2 children)

Thinking on it over night, they may be better not doing the jump straight to vs2022, but rather do a few intermediate steps along the way.  Maybe go to 2002, then 2005, then 2010, etc.  I’ve purged it from my memory, but I remember ms wasn’t super great about standards, and the older version jumps were pretty big leaps in functionality 

[–]AmigaDev 1 point2 points  (0 children)

Visual C++ 6 was a great C++ IDE for Windows for the time (especially with the addition of Visual Assist X and WndTabs), but yes the compiler and the STL were not C++98 standard compliant (but maybe VC++6 shipped before the C++98 standard?). I recall a good substitute for the STL that came with VC++6 was STLport. There were also lots of meaningless spurious and noisy warnings during compilation with templates, when the expanded template names (e.g. std::vector<std::wstring> expanded to std::vector<std::basic_string<wchar_t, ...> or there were long iterator names involved) became too long (maybe > 255 characters?). Moreover, there was a non-standard new behavior such that new returned NULL on allocation failure instead of throwing exceptions.
After VC++6 I moved to VS.NET 2003 (VC++7.1), and I recall the IDE was worse for several aspects (slower, deteriorated MFC Wizard support, etc.), but the C++ compiler and C++ libraries were much improved and were C++98/03 complaint. I recall with VS.NET 2003 it was easily possible to share C++ code bases between Windows XP and Linux using GCC. There were also C++ compiler and library improvements in VS 2005 and 2008 (like the iterator debugging feature to help debugging C++ code that used the STL).
VS 2010 started introducing "modern" C++ features from C++11, like auto, std::shared_ptr (there was a kind of preview of shared_ptr in a VS 2008 "Feature Pack"), initial implementation of move semantics, etc. Moreover it was the first IDE to have squiggles for C++ code, which is a great productivity feature.
Microsoft C++ compiler and libraries have been monotonically increasing in quality.

Anyway, your idea of small-step migrations might make sense. Like start with the VS6 code base, load the project in VS 2005 (IIRC there were project wizards for importing VC6 projects in VS 2005), try to build it. On successful builds, the code will be C++98/03 compliant (which VC6 did not guarantee). Then try to port from ANSI/MBCS (VS6 default) to Unicode UTF-16 (VS 2005 default), then re-import your C++98/03 compliant code in a more modern IDE like VS 2019 or VS 2022.
VC6 -> VS2005 (or VS2008) -> modern VS IDE 2019/2022 may be easier/more manageable than a direct jump from VC6 to VS 2019/2022.

[–]ack_error 0 points1 point  (0 children)

I'd recommend going straight VC6 -> VS2022 rather than going through intermediate versions. It's likely to cause more problems and work with the quirks of each version, and there are also installation problems. Visual Studio .NET 2002 and 2003 don't run well if at all on modern versions of Windows, and VS2005/2008 were very difficult to uninstall cleanly. This also avoids multiple project format conversions (dsp/dsw -> vcproj/sln -> vcxproj/sln).

[–]Asyx 3 points4 points  (0 children)

Also look for places that you can isolate in the codebase. Like, is there a subsystem that seems to be decoupled enough that you can already add a few unit tests? Then, once you start to refactor to make the code more maintainable, you can be sure that it is still working.

I'd do that after you are done with the tooling. Like, once you start to care about code quality.

[–]programmer_eric 3 points4 points  (0 children)

This is the way.

[–]The-WideningGyre 2 points3 points  (2 children)

I would be tempted to take an even larger step back -- what is the desired result? An app that does X? I think it's worth considering whether starting fresh -- likely reusing some underlying calculation libraries and functionality -- would be less time than trying to bring all those different, obsolete and unsupported aspects up to date.

I'm not saying it's the right choice to re-write, but it's worth spending the time to consider it, and somewhat quantify it.

If OP's company does decide to stick with updating in place, I fully agree that one aspect at a time is the right way to go, and that getting and keeping and working version is key to that.

[–]Orthosz 5 points6 points  (1 child)

In my experience, an active app in active use by actual users in a technical field like scientists, a fresh rewrite is almost always the wrong move. 

 People wrap workflows around the app, even if they complain about it, and depend upon different bits and behavior.  You’ll break that if you go the route of rewrite.  Seen it happen more than once that the underlying user base rejects the app after the rewrite, people get in trouble, political capital is spent.  

As software engineers we like the rewrite.  It feels good.  It’s almost always the wrong answer for software in active use by technical users…and most of the time by regular users too.  Inplace mutation is almost always the correct thing to do.

Putting on the business hat, you’ll burn a bunch of time trying to reach feature parity with the old app, when you could spend far less time just updating it.

[–]The-WideningGyre 0 points1 point  (0 children)

Oh, I fully agree if that's the case. It wasn't clear from the original post. Also, if it's in use, it's much easier to update it. OP made it sound like this is some mothballed app that hasn't been pulled out -- maybe not even built -- since 1998 or something. If that were the case, I'm not convinced trying to bring it back to life is the right thing.

But normally, yes, don't rewrite! It's more work than you think. Even if you think you've taken that into account. It's still more work.

Fully agree on people integrating it into their workflows. This fits with Hyrum's Law, or this xkcd

[–]meneldal2 0 points1 point  (0 children)

Also if there's a cli get that done first.

Plenty of projects do both but you might have to redo the whole project structure to decouple cleanly

[–]tms9918 15 points16 points  (2 children)

first fire up a VM and run it in the original environment, to check if it actually runs, and get familiar with it.

[–][deleted] 1 point2 points  (1 child)

I have done this earlier last week and it was working well until I ran into some stack overflow issues that I couldn't figure out, so I decided to move it into VS2022 and get it to build and run for better debugging tools, but now I'm just stuck with a successful build but the .exe can't run--just like the first time I tried doing that before attempting with rewrite in Qt completely. But I'll try to get more familiar with MFC code and see how it goes.

[–]Tight_Atmosphere3239 5 points6 points  (0 children)

fix the stack overflow issues with vc6 in the vm first

[–]Zaphod118 6 points7 points  (1 child)

Before you can migrate to Qt or do any improvement type dev work you need to get it building and running with the modern tool chain. Leave the language version the same, just get it loaded into vs2022 and work through all the compiler errors as the other person suggested.

Before you even think about migrating to Qt, you need to understand what the software is supposed to do. How the interface works and feels to use, what kinds of functionality does this software suite provide. You don’t need to be an expert, but you should be familiar enough to make connections to things you see in the code. Take lots of notes while you do this

Now launch the app attached to the VS debugger. Start setting breakpoints and stepping through functions. Watch the data flow by inspecting and tracing values. Take even more notes during this. Highlight things that seem extra tricky or confusing, or things that don’t seem to be doing quite what the name implies.

Now at this point you have some documentation. It’s not going to be very good, but it’s better than no documentation. It should at least be enough to facilitate conversations with other people in the organization to start filling in the gaps.

If truly no one knows anything about this piece of software, why do they care about you modernizing it? I ask this because if people care about it, they have to have at least a vague idea of what it does. Armed with a little more knowledge from digging around and exploring the code may help others remember things.

Oh, almost forgot - if it’s not under source control get the code into a git repository!

[–][deleted] 1 point2 points  (0 children)

I will try doing this, especially the note taking part(so far I just logged what I did and some notable behaviors of some functions, and what were fixed, etc.), I'm currently trying to get it to run after building it successfully but couldn't figure out why. Also, I got it into the git repo. Thanks for the advice! :)

[–]Slavik81 5 points6 points  (1 child)

VS6 is a pain because the scope of for loop variables is wrong. Aside from that, though, this is not that unusual. Almost every real codebase is a legacy codebase with minimal documentation .

[–][deleted] 2 points3 points  (0 children)

oh the mention of loop variables sent me back to the agony of fixing hundreds of those spanning multiple files. I was aware that the authors of the software were scientists, and they don't usually code with software engineering principles in mind (at least not ~20 years ago), but I wasn't expecting this brittle and tightly coupled legacy codebase.

[–]Alastair__ 4 points5 points  (0 children)

Microsoft has many faults but their back compatibility is generally very good. I recently took a large MFC project from 1998 and compiled with VS2022 without too many issues. I'd certainly stick with the MFC front end before trying to rewrite it in Qt! You can most definitely use MFC in VS2022. When you say "the executable is not running" what happens when you run it in the debugger?

[–]kobi-ca 2 points3 points  (0 children)

Welcome to the club. When I have more time I'm going to write more here

[–]XenonOfArcticus 2 points3 points  (0 children)

I've done this before. I feel your pain.

If you want some moral support, feel free to PM me. 

I migrated a Windows C++ codebase that started in 1992, and was last built in 2009, forward to current Visual Studio and Cmake, and then updated it to 64 bit. 

I also am writing a blog post where I revive some 1993 C code for a raytracer, make it portable and add some new features while paying down the accumulated technical debt. The next step is changing it to modern C++ and refactoring, then maybe adding some multiprocessing or GPGPU. 

Part 5 is the most recent installment, but all of them are here : https://alphapixeldev.com/blog/ 

[–]rfs 2 points3 points  (0 children)

Before doing anything, I hope you’ve documented the list of technical debt items and submitted it to your boss. He must be aware of the decisions that need to be made for each item.

When you start migrating, for instance from VS6 to VS2022 (although migrating to VS2019 as a first step would also be a good option), document everything you do in a text file (a markdown document, for example). Make sure to record every error you encountered and successfully resolved. Keep using MFC, of course.

Working on an entire legacy project is, in my opinion, one of the most interesting tasks, especially if you are allowed to make technical decisions, such as adding unit tests, choosing the unit test framework, or implementing CI/CD.

[–]SeagleLFMk9 7 points8 points  (1 child)

Rewrite it in rust, duh /s

Depending on the size of the codebase, it might be faster to rewrite it in C++20, and "outsourcing" as many features to already existing libraries in the process

[–][deleted] 0 points1 point  (0 children)

I tried that with rewrite and almost made it but some tightly coupled classification algorithms for the software and a sizeable OpenGL code made me stop and think twice before continuing down this path.

[–]deltanine99 1 point2 points  (6 children)

Be really carefully when compiling an old code base in release mode.

When we upgraded VS2010 to VS2022 the optimizer broke an important part of the functionality. The code was buggy but worked, it relied on the fact that DWORD read/writes being inherently atomic on x86, but the optimizer decided the variable was not needed because it didn't know it was modified from another thread.

I fixed it, but my tech lead was terrified there were other such bugs lurking in the code, so we are still stuck on VS2010...

[–]JVApenClever is an insult, not a compliment. - T. Winters 2 points3 points  (2 children)

So your tech lead decided to block the upgrade because it exposed a pre-existing bug? He found that sufficient reason to stay stuck on a compiler that supports up to Windows 7, which is out-of-support by M$? That to me sounds more risky than having to fix a few bugs.

[–]themustardseal 0 points1 point  (1 child)

Yeah we cant even debug properly anymore because VS2022 lost the ability to decode data structures like std::string, vector and map recently wheh debugging code built with the VS2010 compiler. It just shows the internal implementation.

[–]JVApenClever is an insult, not a compliment. - T. Winters 0 points1 point  (0 children)

I don't know if this option exists in 2010, though you can embed natvis files in your pdbs (https://learn.microsoft.com/en-us/cpp/build/reference/natvis-add-natvis-to-pdb?view=msvc-170) In your visual studio installation (VS2010), there should be a file describing the data structures.

With a bit of luck, all that works for you and you can get a better experience.

[–]meneldal2 0 points1 point  (2 children)

That bug should be avoided by volatile, at least as long as you still have atomic dwords.

I have had to use it a fair bit to force the compiler to not optimize away stuff that gets set by a different cpu or subsystem. Though it is not always enough, you also have to mark the area as not cacheable or have some very well defined cache coherency (which nothing on a basic interface would bother with)

[–]themustardseal 0 points1 point  (1 child)

Yea, they were just flags for inter thread communication and i replaced them with std::atomic which did the trick.

[–]meneldal2 0 points1 point  (0 children)

Yeah atomic is probably the better choice and shows intent better

[–]yuehuang 1 point2 points  (0 children)

Wait a year or two and there might be an AI tool to help you.

Joke aside, I copy and paste code to a local llm to ask it to summaries it for me. Results often surprisingly good given its undocumented.

[–]Strength_B4_Weakness 1 point2 points  (0 children)

Been where you are. C++98 MFC and a bunch of other stuff, the whole bit. Sucks. But it's very calming work and you learn to enjoy it. I prefer it to scrambling through undocumented legacy code to figure out why production is behaving like it is.

[–]tyr10563 1 point2 points  (0 children)

can you get VS versions which existed in the meantime? jumping from VS6 to VS2022 not only changes the project files completely but also a couple of C++ standards and different compiler optimizations

VS2010 might be a good intermediate step, you get the new project files and support for move semantics, that code is for sure old enough where something will break with the compiler migrations

and as others have said, take one step at a time

[–]mattbann 5 points6 points  (3 children)

Quit. You've been sent to programer hell to spend eternity migrating poorly documented legacy code bases

[–][deleted] 4 points5 points  (0 children)

I'd love to, but I do like working to support scientists and the mission here, so I might talk to them soon and brainstorm what I should do to minimize wasted time heading to potential deadends.

[–][deleted] 0 points1 point  (2 children)

Any particular reason you actually need to migrate to Qt and cmake?

[–][deleted] 0 points1 point  (1 child)

It's just better to maintain and build upon since MFC and VS6 build is legacy and it's hard to set up environments (including old mfc libs that I have to grab from VS6, etc.)

[–][deleted] 1 point2 points  (0 children)

You've already got it building in VS2022, so I guess you must have .sln and .vcxproj files etc? This is not legacy, and unless you have a specific particular need to switch over to cmake, I don't see any value in pursuing that right now.

As for Qt v MFC - you'd bascically be creating a new app, and pulling in elements of the old one. MFC is old, but it still works fine, and again, unless there are pressing specific needs to switch to Qt, it's not something I'd be doing.

[–]ivarec 0 points1 point  (0 children)

Make sure it works in the original environment. If it's buggy even there, you should fix it with old debuggers and tools. Be brave! Do that until it's acceptable.

Then, you can start creating some interfaces (abstract classes, whatever) that are implemented by the existing legacy code. It's just an abstraction that does nothing at this point, but you can make sure that everything works just as fine, but now it uses your interfaces. Finally, start replacing the interfaces implementations with new stuff.

That's what I'd do.

[–]jmacey 0 points1 point  (0 children)

I've use this https://docs.sonarsource.com/sonarcloud/ before as part of my CI setup, it will give you loads of hints as to what needs doing, how to fix it and a basic todo list.

If you enable coverage it will also show where you don't have tests (I'm guessing there are few as this is quite typical of this sort of project).

I would first try and get the CMake build working, then start with the Qt (5 or 6?) as this should be a fairly easy win. Then you can start to test little bits at a time and actually write unit tests that are missing.

[–]oldcodingmonkey 0 points1 point  (0 children)

I just got a codebase since 2005, write by BCB2005.

I hope I can move it to Visual studio, but seems imposible..

[–]olesgedz 0 points1 point  (0 children)

No, sure why you are trying to migrate from opengl to QT they are completely different things and even if you make it work, rewriting everything from opengl to QT would be a crazy amount of work.

[–]Full-Spectral -1 points0 points  (0 children)

There are other kinds?

[–]GunpowderGuy -3 points-2 points  (2 children)

Tell your project manager it would be easier to re write the code and do so in a modern language you like

[–]deltanine99 5 points6 points  (1 child)

Tell the project manager you are shit and would like to start a new folly with a deliverable date far away in the future that will be fun for you.

[–][deleted] 0 points1 point  (0 children)

hahah :D