tags:

views:

306

answers:

11

I'm very much against rewriting an application if it can be avoided. I understand the rule that 9 times out of 10, it's better to refactor, but I'm in a situation where it might be the one time in ten, and I'm looking to find that line.

The current situation is:

  • I took over the maintenance of a VB6/SQL application.
  • The total lines of code is 75-100k (code-behinds, modules, and classes).
  • The original developer left, so it's just me, and there's no opportunity to expand the team, at least for a few years.
  • There was no architecture to the program (just straight SQL calls in plain text in the form code-behinds).
  • Doesn't try to follow the DRY or OAOO principles.
  • Database had some primary keys, but no foreign keys.
  • Before this system was in place, everything was managed in big spreadsheets, so this system really is a huge improvement over what they had, but doesn't do what they're envisioning.
  • I was able to write myself some tools to replace all literal instances of table names and column names with constants and look-ups, and I wrote a quick code gen script to generate those constants and look-ups from the database, so now I can safely make database changes and see everywhere that broke. I have started normalizing the database "around the edges", but it's like 3% of the way there.
  • There are no unit tests, so every time I change the database, I basically have to rewrite whatever logic sits on top of it, and I use the two versions to compare functionality and make sure it's the same. So far so good.
  • I started by just trying to fix critical bugs to stop the bleeding, and I can safely say that's mostly done, so now I'm stepping back for a moment to look at the big picture.
  • Management is supportive and reasonable in their expectations.
  • The long-term goal is to convert it to .NET anyway...

So, I'm weighing these options:

  1. Continue normalizing the database and modifying the VB6 app as I go (ends up being a piece by piece rewrite)
  2. Put the VB6 one into a maintenance-only state (no new features), pick one functional module at a time and rewrite that part in .NET on top of a normalized database structure.

My thought is that if I choose option 1, then at the end I just have a VB6 app that they still want to upgrade to .NET, and I've looked into that and it's costly and time consuming, and even with the tools you'll still get something that's somewhat of a Frankenstein. If I go with option 2, I believe I can be done sooner, and I'll jump right to the target technology.

In the small scale pieces that I've already rewritten during my normalization process, the result has been an improved module over what was already there, so there is value being added during the rewrite.

The existing app, for all its flaws, is a great talking point for discussion. The people using it can tell me what's working for them and what isn't, so there's certainly a lot of value there that way.

So, does this qualify as one of those "one in ten" times, or not?

A: 

The long-term goal is to convert it to .NET anyway...

Sounds like you'll just be maintaining old code until it's time to port it to .NET. So save some work and start today, rewrite. My 2cents.

Make sure the existing app can still be used while you rewrite as sounds like it can't be done in a day or two :) The "maintenance-only" state sound like a good idea to me.

Select0r
+1  A: 

I think it might take you quite a while (too long) to rewrite a 100KLOC application (including reverse-engineering the specifications, and testing), without help.

ChrisW
I don't have my copy of _Software Estimation_ here with me, but I think that 100KLOC might be approximately 5 man-years, or something like that, you know, on average. So you'd have to ask yourself: "Do I feel lucky: above average, able to beat the odds?" Well, do you?
ChrisW
Ha, well it only took the other developer 3 years to write this much!
RewriteOrRefactor
+1 I have [Rapid Development](http://www.amazon.com/Rapid-Development-Taming-Software-Schedules/dp/1556159005) here. A 70-100kLOC business product would take 61-93 person-months on average. Of course, that's for a *finished* product, you know, debugged and working :)
MarkJ
Realistically if you removed the redundancy, it's probably only a 30k line app in VB6 and shorter in .NET.
RewriteOrRefactor
A: 

Maybe. For starters, you say "Doesn't try to follow the DRY or OAOO principles" so right away there is probably a lot of redundancy that could be refactored, but if you re-wrote in .NET, you wouldn't have to refactor. Also, the LOC count is probably bloated due to this issue.

If you continue a slow, refactor-as-you-go approach, you will still need to re-write in .NET. The source of the re-write will eventually be neater and cleaner (and probably more sanity-preserving) but you will still eventually need the whole re-write.

If you put the app into maintenance-olnly mode and then start coding the new app, how long will it take - and how long will new features be delayed from being put into production via the existinc app? Assume that there will be some critical features that will need to be added so you will have to add them anyway.

FrustratedWithFormsDesigner
Your point about the bloated LOC count is very true. I estimate probably around a factor of 3 reduction, at least, even if you got rid of the redundancy staying in VB6. In .NET you have better tools thanks to some new functional elements, and you could get a higher factor. My goal is to end up with something under 20k lines.
RewriteOrRefactor
+3  A: 

Start converting the different modules one at a time, phasing them out of the VB6 application.

Prioritise according to what is mission critical/strategic, so you can also add value as you go along.

Since the database will be the integration point, make sure you fix the relevant parts (relavant to whatever you are working at the time), so you are building on a solid foundation.

In the short term, you will have more work (both VB6 code and .NET to maintain), but with a cleaner architecture, you will be able to start moving away from the legacy application faster and faster.

This way you will have both systems running at the same time for a while, so you have a fall back, and you can add proper structure (architecture, database integrity etc).

Oded
.NET has rich COM interop support, so no reason rewritten VB6 modules couldn't be directly replaced with .NET, so you are not maintaining both in parallel. At some point you replace the shell of the application, and .NET components in VB6 is replaced by VB6 components in .NET application.
Richard
@Richard -that would be like building on top of a house of cards, from what the OP describes.
Oded
@Oded: true to an extent, except the current house of cards *works*. Better to change incrementally, adding unit tests, ... into something architecturally better than starting from scratch where you'll have nothing working (functionally) for ages.
Richard
@Richard: Simply not true. a) it only marginally works, and b) I can rewrite small pieces and have them deployed in 2 to 4 weeks, including new functionality.
RewriteOrRefactor
@RewriteOrRefactor In that case why ask the question? (Also the fact that it is a number of independent pieces that can be re-written independently makes a big difference, I was assuming a single monolithic application.)
Richard
@Richard: Pretty much any application can be dismantled and rewritten piecemeal in another technology if it's on the same platform. I thought that was a given. The question is whether to refactor first (move code out of form into a BLL, then write unit tests, then refactor more, then normalize the DB, then fix and refactor, etc.). Rewrite means to throw out the existing VB6 code (other than using it as a functional reference). Both options are done one piece at a time.
RewriteOrRefactor
+5  A: 

Having been on a project where we did the app and the database all at once I would recommend trying to get the database correct first and then doing the app after that.

Trying to do both simultaneously could prove very painful. However without being next to you looking at the code it is always tough to say for sure.

I just always feel that if you have the database and you can rely on it you should be able to rewrite using integration tests and then unit tests so much more reliably.

Once you have the database solid I would think a best case scenario would be to try and modularise the code into separate and reusable assemblies (which you should be able to use in .NET) and then gradually mograte those to .NET from VB. That might not be possible if the code is horrendous smelling spaghetti!

ArtificialGold
The act of moving the code out from codebehind into modules would be massive in itself. Not least because in a lot of cases it's loading collections of data from SQL into an MSFlexGrid and then manipulating the data in the MSFlexGrid based on column numbers, not on some constants, so gleaning the meaning of "If Trim(MyGrid.Matrix(MyGrid.row, 15)) = "1" Then..." is really tough, and doing any kind of automated fixing is also tough, especially since the Grid is most likely referenced directly from other forms and modules. Oh, and using the With...End With construct makes it more fun. :)
RewriteOrRefactor
ArtificialGold
Yeah, that's a good book! :)
RewriteOrRefactor
+1  A: 

It sounds like a major job to rewrite the application from scratch, especially since there are no unit tests and you are not the original author. I think that an incremental approach is probably the best, starting with adding unit tests and re-factoring the code so that can be unit tests in order to capture the functionality of the application. Then incrementally improve the app as time goes by instead of putting on hold. A rewrite is often easy to underestimate if you are not the original author as there could business logic in there that is not obvious.

Anders K.
A: 

First off - you poor s*d. never a nice place to be marooned on.

However, it really sounds like you've grabbed the bull by the horns and pursuing a steady course. I would be tempted to put into the mix that VB6 will no longer be supported next year (as far as i can remember) which will mean that should the application become broken as a result of os changes, then you'll basically be stuck. That aside, i'd try to see how much parallel developemnt you can pursue with this in .net as the interop shouldn't (famous last words) be too difficult to work out.

as you're on the precipice, i'd say leap but with caution.

I'll add more details when I'm able to jump back to the topic.

jim
Your memory is letting you down. The VB6 runtime is [part of Windows 7](http://msdn.microsoft.com/en-us/vbrun/ms788708.aspx), so mainstream support will continue until at least [end January 2015](http://support.microsoft.com/lifecycle/search/default.aspx?alpha=Windows+7).
MarkJ
Ha, thanks for the sympathy, but really it's not that bad. As I said, the management here is very reasonable and thoughtful. We have good communication going. Also, I think MS has promised that VBRUN-whatever.dll will continue working throughout the lifetime of Win7, and I'm running the app right now on Win7 so we should be ok.
RewriteOrRefactor
sorry mark, i was referring to actual support via m$ themselves. but maybe that's been extended too, given the win7 runtime scenario.
jim
A: 

I'd choose the 'freeze and write a new app' route.

  • Leverage as much VB6 code/methods as you can, or as you see fit, from the VB6 app. Sounds like you might go VB.NET, and some code might survive the upgrade. Either way, there are code converters that can help.

  • Start redesigning at the database. Write scripts to transform the data into a normalized form that makes sense to you.

  • Then start a module-by-module migration to a new application. If you can help it, don't make any more maintenance changes/fixes/enhancements to the new app.

p.campbell
VB.NET or C# doesn't really matter. I've written sizeable enough apps in both. I do have a new "target schema" mapped out. It's so completely different from what I have now, that's what actually triggered me to go down this decision path. Writing scripts to migrate the data is not a serious issue.
RewriteOrRefactor
+3  A: 

That was a great write up. However IMHO its missing an important piece. What is the value of the rewrite in terms of the customer and what are the costs and risks. I'm guessing here but it might be the following

Customers may get the following

  1. faster defect/enhancement resolution.
  2. Lower likelihood of new defects during deployment
  3. Larger developer pool to hire from
  4. May be able to implement features that were impossible in the past

Costs/Risks

  1. Near total loss of current investment
  2. May introduce defects that were solved in the past.

You should consider what you can do to mitigate the risks and also see if the value your adding outweighs the risks and costs.

Conrad Frix
RewriteOrRefactor
+1 Another benefit is "keeps developers happy, making it easier to retain good developers". It's a valid customer benefit
MarkJ
And don't forget the biggest costs and risks: almost no new features while the rewrite is in progress, also risk of severely underestimating the rewrite task.
MarkJ
@MarkJ: remember, I'm talking about a module-by-module rewrite in .NET, adding features at the same time, so it won't be a huge long wait for new features.
RewriteOrRefactor
Yes, what MarkJ said: I would have guessed those are among the bigger risks (assuming that project and deployment management is good enough to avoid the biggest risk, which could be that the new implementation is worse than the old).
ChrisW
+8  A: 

I had a project that was originally written in VB6 and they hired me to convert it to .NET. I recently left that job. I'm fairly convinced that the program probably shouldn't have been rewritten.

Here's some factors to consider if you take the re-write approach (based on my project)

  • It will not be a straight rewrite. Once word gets out its getting re-written new feature requests will pop up and there will be a fair amount of redesign/re-architecting
  • People will not accept the "maintenance-only" mode with out a lot of resistance first. For a while, every bug will be "mission critical"
  • Porting a VB6 app to .Net should NOT be viewed as easier than porting a C++ app to .Net, or a Haskell project to .Net. There is some commonality, but in the end, it's a 99% different code base (possible 1% for existing equations)
  • Assuming this program is in production (even internally) your new program will very likely not measure up, at least initially. You'll hear "But in the old way..." or "It used to..."
  • Assuming your customers are not other IT people, they WILL NOT understand:
    • What is taking so long
    • Why it isn't just like the program (in look & feel) of the old one
  • While the current app was developed over time, it will likely be expected that your app will have 100% of the functionality on go-live, and in less time.

Those are all experiences from a real life migration from VB6 to .Net. I was the only .Net developer. There was no resources to hire additional help. The main differences between my situation and yours is 1. the original developer was still there - just in a new role (making it harder at times) and 2. The original app didn't need a lot of bug fixes.

I think if I had it to do all over again, I would try to create some .NET dlls and incorporate those in the VB6 app. Piece by piece converting to .net. So perhaps you move the data and business logic for Accounts Receivable to .NET. All the other aspects stay the same. The GUI, the other features etc. Then after that's rolled out and marked as complete, take on the Shipping section and do the same thing. The final step is to create a new .NET GUI that uses your DLLs.

This gives you a couple benefits

  • In the end the application is written in .NET
  • Allows you to slowly roll out your changes. You won't be debugging shipping and accounts receivable and hr etc all at the same time the week of "go-live"
  • Allows you to use more modern tools without wrecking the whole program. Want to use NHibernate, PLINQ or MEF? Do it one module at a time. Learn the tool and make small steps
  • Allows for flexibility on the GUI. In the end, you could do WinForms, WPF or a Web project, all utilizing your core DLLs.
  • Doesn't throw a ton of changes at the user all at once. You've re-written that accounts receivable portion and the user has no clue, because the GUI is the same. Then when you roll out the GUI, you KNOW that your backend is working.
  • Will make future refactoring easier. You've already broken down your project into bite-size chunks. You can further work on them knowing what they effect etc.

I would be VERY weary of deciding as a single developer to re-write this application and deliver them a working product. I think a conservative estimate of that (assuming no scope creep etc) would be 24 months. Probably more likely 3-4 years.

The project I left had been worked on for 3 years and was servicable, but was not yet a 100% replacement for the original app. Like I said... even though it would have meant me not having a job, I don't think it should have been re-written.

taylonr
+1, Ok that's kinda long, but it's well said and you hit on things I've seen in other VB6->.NET projects. Some excellent points.
FrustratedWithFormsDesigner
Just to be clear, I'm *not* advocating a 100% rewrite in one shot. I would take one module at a time just as you suggest, carve it out of the VB6 app, migrate the data into a new normalized database structure, and write a new tiered module in .NET that just does that part. They would launch the new module from the old VB6 app, in the same place where they would have gone to use the existing functionality. Also, I've found best results are to make sure I hit a bunch of user pain points at the same time as I rewrite a little bit, so they see that they're getting something too.
RewriteOrRefactor
In all honesty, you'll learn a TON about software development on this project. Most will have nothing to do with code :) As someone else said, make sure you nail down the DB before starting code etc. Minimize your variables (DB, GUI, export data format, code base, etc)
taylonr
+1. The official [advice from Microsoft UK](http://msdn.microsoft.com/en-gb/dd408373.aspx) is that they only recommend a complete rewrite from scratch of VB6 code in VB.Net for a small number of situations. There are better options, like automatic migration with commercial tools, or piecemeal,partial replacement module-by-module.
MarkJ
+1  A: 

1). It's VB6 which MS stopped supporting development of two years ago (ref). They don't support it so neither should you, and this is why software is never finished.

2). The volume of code is not a factor - the difficulty of refactoring or rewriting an application both scale with size.

3). Not having the original architect means you're going to forever be guessing at intent and tripping over things you didn't know were a problem. I would characterise this as a severe risk if you chose not to rewrite.

4). The architecture such as it is is clearly junk, and another severe risk. Everything will take you longer to do so the efficiency of refactoring over rewriting will only last a short time anyway.

5). The lack of unit tests is yet another severe risk since you won't be able to trust any changes you make. This is the first thing you need to change whatever approach you take.

6). If you have management acceptance and a strategic desire to get to .NET then you really don't have any reason not to rewrite.

So that's several severe risks with not rewriting and no given reason to avoid it. I would try and break the application into logical services and draw separations of responsibility where you can. Remove and replace chunks of business logic cauterising the old application with anti-corruption layers to try and minimise the turnaround time and also to demonstrate the validity of a rewrite.

Good luck soldier.

annakata
Most of the "why" still exists, thankfully. The management team involved in asking for the system is still here, and I can just say, "why does it do this?" and they always give me a pretty clear answer, along with reasonably good feedback about what they'd actually *like* it to do. Interesting link about the anti-corruption layers, but actually due to the lack of normalization and lack of generalization, there aren't many connections between various pieces of the existing database (for instance, there's a different table for each contract type). So cauterising isn't too painful.
RewriteOrRefactor
Anti-corruption can cut both ways - you can build your domain services to reference ideals, rather than realities.
annakata