How to migrate ugly and undocumented VB6 Code to .NET
I know that there are already Questions about VB6 migration, but the code base of my project brings some new questions here.
I have to say the Code quality, structure and architecture is just a nightmare. There are 2 Big Projects: Nr.1 with 40 Forms, 40 Modules and a few class files, this EXE is kind of a "base system". Nr.2 with 80 Forms, 20 Modules and a few class files again, this EXE calls functions form the "base system". Then there are ~10 other Projects with GUI (1-3 Forms each) and another 90 non-GUI Projects, most of them EXE Files, some DLLs. The DLLs are written in C, C++ and VB6.
The Code has grown evolutionary since 10 years, and written mostly by 1 (bad) developer at a time.
- Functions with 500 Lines (and more) are very common.
- 90% of the GUI components are named text1, command2(1), …
- copy and paste is all over the place e. g. an EXE project (without GUI) with 5000 lines of code was copied and the only change in the copy was to send files per Mail instead of FTP (there are 2 further copies of the same project also).
- I once had a small form (15 fields), where I should solve a small problem(max. a half hour thing normally), every time I changed something, it either didn't work, or produced new errors in the form. After 2 days, I decided to rewrite the form completely and from ~20 SQL statements in the old form, only 2 survived in the new one.
- don't even ask about comments in the code …
I took over the project a few months ago and I'm the only maintainer. There is a constant, (but low) flow of Change Requests and errors and we get a maintenance budget from our customers to keep the software running and "up to date" in terms of legal requirements.
My Options
1) Rewrite from scratch - In that case I could write it in Java for portability. The problem here is that besides some (old) user help, there is no documentation, so the ugly code is the "documentation". There is one Person who has the high level know how what the software should do. Also it's hard to convince management about doing it, even if there are huge cost savings in the long term, there are political issues. I also can't do that one (vb) project at a time because the database structure is no better than the code i.e. has to done from scratch too. So I can only change the whole software at once.
2) Migrate the code to VB.NET / C# Migration of the main Projects first, I tested that already and got ~2000 upgrade comments from Project Nr.1, most of them things like Screen.MousePointer changed, functions with variant return values and so on. My Idea here is after the convert, create classes for DB abstraction, change the code to use these classes and do refactoring, migrate and change the other projects too and when all the code uses the DB classes, change the DB structure.
3) Refactor the code in VB6 whenever I have to change something there anyway (I'm already doing this partly) and at some point refactor also the rest. That way it's more easy to see the original functionality, because it's original code and when there are errors, it's obvious that they can't be results of the migration. When the code is refactored (I assume it will be 50-75% smaller t开发者_如何学Gohen also) it's easier to migrate it to .NET. Then change DB structure (and then do another round of refactoring…).
There are some bigger changes to do in the future (make it compatible with Win7, and another big CR which affects big parts of the code), so there would be a good opportunity to do these changes, as I'll have to go through lot's of the code anyway.
My Question is who has experience / hints for migrating bad, ugly code? Which options would you suggest?
I had to go through the same thing (Massive VB6 app with no documentation and horrible code.). The only safe and reasonable route you can take is number 3. To improve something you must first understand it. If you take route 1 or 2 you are guaranteed to be left with a horrible mess.
Remember to always keep the idea in the back of your mind that your end goal is a full migration to .NET. As you are refactoring think about how VB6s awful OO support will look like in VB.NET or C#. If possible shift your code around to make the migration easier.
You may want to consider shifting a lot of your core functionality into a .NET DLL and exposing it to VB6 through COM. This will remove a ridiculous amount of code from your VB6 and will hopefully leave mostly business logic.
The most important thing you need to remember is don't be a cowboy.
- Write tests for a module.
- Refactor a module.
- Test the module.
- Release your app.
- GOTO 1
Does the code absolutely have to be migrated? If it doesn't, still serves it's purpose, and is halfway maintainable, just leave it alone and move on.
Your motivation to move the code to a newer language will soon fade after countless hours, weeks, and months of code migration -- especially on the scale that you mentioned.
The idea of code migration is a fantastic idea, conceptually. In all probability, it is a terrible mess and you will hate life doing it.
Think about it, do you hate your life now while fixing a defect? Escallate that by 100x for the migration project.
Most businesses could really care less about what's under the hood as long as it works. Remember, they aren't developers, don't care about how much of a pain it is to fix it (as they aren't the ones doing it) and motivating a the business to spend tens-of-thousands of dollars to bring code up to date, simply doesn't make any business sense.
Experienced similar migrating 14 year old code to VS2008
Here are some tips:
- Remove dependancies on 3rd party components.
- Consider staged migration e.g. using reverse interop.
- Colours and Fonts need to be dealt with by hand.
- Watch out for uninitialised objects in arrays after migration to .NET
- Change On error to Try Catch
- Make use of Microsoft.VisualBasic.Compatibility.VB6 Namespace where needed.
- Keep refactoring to a minimum until all code converted to .NET
- Watch out for non zero based arrays in your VB6 code.
- Refactor (minimal) your code in VB6 so it can go through the code conversion wizard.
- Watch out for 1 based VB6 controls e.g. ListView
- Use synclock to avoid threading issues.
- If you are doing manual refactor of a sub then be careful of the changes in data type sizes especially if you are working with file IO.
There is a company which can help you out with the whole process (although we didn't use them) http://www.vbmigration.com/
Check out M. Feathers' "Working Effectively with Legacy Code" -- it discusses the pitfalls of enhancing, refactoring and migrating and untested code.
you did a great job formulating this question. Thanks for asking! And thanks to the stackoverflow community for posting thoughtful responses (as usual).
If this is a fairly large codebase, and it sounds like it is (50-100 inter-related VBPs, 200-500K LOC) then you should consider investing in migration tools. Consider this analogy: if you had a large amount of data to convert, even if the source data was dirty and very different from the desired format, you would use tools. If someone suggested you should just re-enter the data, or even do a rough cut conversion and then fix it by hand, you would think they were nuts. Also with 120 forms, the application sounds like it provides a fairly significant amount of business functionality. That business functionality has emerged over many years of use, and, like it or not, the VB6 code represents a complete, formal, and production-tested specification of that functionality as well as myriad technical facts about how the app works behind the scenes. Regathering, recoding, and retesting all those functional/technical requirements "from scratch" is very expensive and difficult. In order to manage the cost and risk of the project you must "cash in" the legacy code. The question is: how to do this and also ensure lower cost of ownership after the migration.
The challenge has less to do with the size of the codebase than with the details of redesign needed to adapt to and take advantage of .NET. You need to identify the specific things that make the VB6 code "ugly" code, why it is "ugly", and how you must/should/want to do those things differently in .NET. Once you start formulating these redesign requirements you can begin implementing them in your conversion process so they show up in your .NET code. The general approach we advocate is called the "tool-assisted rewrite" and it is done follows:
- run the translation
- build/review/test the generated code to identify/refine needed code improvements
- if the generated code is "good enough", goto finish the job in .NET
- reconfigure the translator to implement the needed improvements (if appropriate)
- goto 1
The output of the tool does not need to be "perfect" (software never is...) it just needs to be "good enough". What is "good enough" mentioned in step 3 is a critical question. In essence it means the code quality -- in terms of being verified as functionally correct and conformant to your standards – is such that the development team knows what it will take to finish the migration and then continue operating/maintaining the application – AND they are confident they can do this within the given time and resource constraints. For a very large codebase "good enough" is "very good" because it is just too expensive and risky to try to finish and subsequently maintain a low-quality migration. In general, the larger the code, and the smaller the budget, the more efficient your migration process must be.
Also some thoughts on "finish the job in .NET": Having a well-formed code in .NET is a major milestone. There are good refactoring, analytics, and unit testing tools from the .NET community that can help you accelerate you efforts to finish the migration. You will be using Visual Studio very early in the process to experiment with and refine redesign, and debug/test the app. Being able to work with your entire codebase in .NET and with Visual Studio early in the migration effort is a key benefit (and a critical part) of the tool-assisted approach.
Of course for this to be feasible, you need to be able to invest in a migration toolset that is specifically designed to support the tool-assisted rewrite. The tool from Great Migrations is the only tool that I know of in this category.
Disclaimer: I work for Great Migrations. There is much more information on our site – including case studies for how the tool was used to help migrate over 1M LOC of VB6 to re-engineered C#, and the gmStudio User's Manual that describes the product and methodology in detail.
From your description of this project, it sounds like it might be possible for you to refactor and migrate at the same time. You're clearly going to be taking the large amount of redundant code and consolidating it; it's likely that as part of the consolidation, you can also rewrite it (the redundant code, that is) in .NET.
This won't work for UI stuff. But it will for database and business-logic stuff. For instance, I haven't seen your code, but I'll bet folding money that most combo boxes in your application that get populated by methods in their form (probably copied and pasted into Form_Load
) that create ADO recordsets and loop through them. That's something that can be refactored into a .NET data-access class and integrated into existing forms with ease. It will also probably allow you to introduce the magical world of caching into this application, which will be nice because that will probably result in visible performance improvements.
And visible improvements are important. Unless your management completely buys into the necessity of doing this, this is a very high-risk endeavor. It's very bad if you find yourself telling your management that a given CR is going to take longer to implement than they expect because of issues in your refactoring. Even if you have complete support, you don't want this situation to arise, but it's a lot worse if your support is partial. Visible improvements will do a lot to mitigate this.
Also, there's a growing body of literature on the problem of refactoring legacy code. You need to be on top of it. The more you know before you dive into this, the better your experience will be. I haven't read Michael Feathers's book myself, but if I were in your shoes it's the first thing I'd do.
Having worked on several migration projects the first thing to do is be clear on what your migration goals are. These can vary significantly from organization to organization, but they help set priorities during the actual migration project. It also helps better evaluate the benefits against the risks and costs.
Like others have mentioned, it's important to realize that the migration process won't automatically improve the quality of the code. So if your main goal is to refactor (and nothing else) then you should be aware that it might be weeks or months before you have functionally equivalent code (depending on the code size and migration complexity).
That being said, .NET does have some very nice tools for refactoring and code analysis, in addition to unit test suites. Automated conversion tools like ArtinSoft's Visual Basic Upgrade Companion can be customized to enforce coding standards or improve the code significantly during conversion. This mixed with other code modification tools like ReSharper will greatly speed up your transition to .NET.
In my experience a migration project is a manageable project as long as you're aware that there is manual effort involved. Commercial tools have assessment modes which can help quantify the migration effort, and give you an idea of what challenges you might face in a migration.
If you prefer a low risk and cost solution, I'd stick to refactoring the code with an eye towards an eventual migration. I have had customers for whom this approach worked. So when it came time to migrate their code several things had been solved already.
Basically, any new custom controls should be written in .NET and exposed as ActiveX controls to be used by the VB6 app. Likewise, new DLL functions should be placed in .NET assemblies that can be used through COM. That way when it comes time to migrate you won't have to worry about those controls or libraries.
Compared to some of the stuff I've worked on it sounds quite small. Nevertheless there is another approach which which will work in conjunction with that shown by PeanutPower above.
The answer is to build a framework that will allow you to cut out sections of the program at a time and convert them one by one.
e.g. Insert a database adapter 'layer' that handles all calls to the database, then one by one, remove each but of SQL and replace it with a call to the db layer. Old calls will still go direct while new calls go through the layer. Eventually all calls will go through the layer and you can change the backend DB.
You can do the same with any other section of the code by expanding the framework 'layer' to cover that functionality.
The key to shifting it from VB6 to VB.NET (for example) is to use the Interop library - here's one article: http://support.microsoft.com/kb/817248
Another, much later thought: Get MZ-Tools and use their analysis tool to find redundant code and get rid of it. I managed to eliminate something like five to ten percent of code in an old legacy product at my company.
Don't rewrite it from scratch. It sounds like it will take you ages and you'll most likely introduce new(old) bugs into the code.
I would look at refactoring the code slowly but simply. Start small. Its surprising how quickly you can refactor the code in to something which is much more manageable. And you should be able to keep the code 'working' at the end of each block of refractoring you do.
UPDATE: Its worth noting that automated migration will only move your problems to a different language.
精彩评论