Fog Creek Software
Discussion Board




Throwing away old code

Hi Joel

The company I work for sells a product which has all its user interfaces written in Delphi. Currently this product only runs on Windows.

We are starting to support Linux as well now.

In your 'Things you should never do, Part 1' article you talk a lot about the merits of not throwing away old code.

What would you suggest we do with our current Delphi code, as it obviously will not run on Linux? The current plan is to rewrite the most important user interfaces in Java Swing.

Regards

Emile Cronje
Thursday, April 01, 2004

Translate it, line by line and procedure by procedure.

Make sure all the knowledge that is embedded in the old code is transferred over to the new code, one line at a time.

Spend a couple of weeks before you start practicing. Create a "translation strategy" explaining how various things in Delphi will convert to Java. Before you start, experiment with how you're going to translate various types of things. Do you use OCXs in Delphi? How are you going to port those? I know there are lots of places where Windows controls are much richer and more functional than Swing controls -- make a policy about how you're going to deal with that. Your translation strategy should explain how to deal with every object and function that existed on Delphi but doesn't exist on the Java side. Here's an example -- I don't know Delphi but here's what something might look like in the translation strategy document from Visual Basic to C++/MFC:

39) Replace calls to Left$() with calls to CString.Left().
40) Replace Variants with COleVariants.
41) Because MFC doesn't have an exact equialent of FormatDateTime, we'll write our own in C++.

When code looks messy, translate the mess, line by line. Only AFTER you have the whole thing working in Java should you try to refactor and clean up.

As you go through translating things, amend your translation strategy document.

If you do it right this should only take 10% of the time that it would take to rewrite from scratch.

Joel Spolsky
Fog Creek Software
Thursday, April 01, 2004

Did Borland kill Kylix?


Thursday, April 01, 2004

Emilie there is a Borland product for Linux named Kylix
http://www.borland.com/kylix/

which is Delphi compatible compiler.

Could you port your project to CLX?

There is also http://www.freepascal.org/ which supports most borland compiler features, but not VCL

Max Belugin http://belugin.newmail.ru
Thursday, April 01, 2004

For that matter, can you run your Delphi apps under Wine?

Phillip J. Eby
Thursday, April 01, 2004

They haven't pulled the life support yet, but they seem to have instructed everyone to "take no heroic measures" to save it.

With that said, before you go and translate a huge Delphi program into Java, you probably should at least check to see if Kylix would meet your needs.


Thursday, April 01, 2004

As tedious as what  Joel says to do sounds, I must agree. I have done it and have gone the rewrite path, and the "port line-by-line" strategy is much less painful.

We ported an embedded product from PL/M to C and from one processor to another. One of my coworkers wrote a PL/M to C converter that required a little babysitting and cleaning up after, but took care of a lot of the grunt work. (Incidentally, that is something you ought to be able to do with Delphi-Java, at least for mundane formatting and similar function calls)

We now have a working, selling product with most of the warts of the old product. We would be nowhere close to shipping if we rewrote from scratch.

When we went to "total rewrite" route, we fairly quickly had something that delivered 80% of the functionality of the old product.  Unfortunately, most of the work was implementing the remaining 20% which had all of the special cases that everyone forgets about.  Had we done the line-by-line thing, these would have already been included.  We could have then spent time refactoring, rather than reinventing and rediscovering.

It would be nice to live in a theoretical world where you have time to do it over and do it right.  However, as bad and old as the old code is, it probably works and has thousands of hours of work and decision making put into it. Don't throw it away.

Kyle
Thursday, April 01, 2004

There is Kylix which is the official Borland way of running Delphi stuff on Linux/X11. Have you tried this? Why didn't it fit your requirements?

There is also Lazarus (at Sourceforge) which is a Delphi close for the Free Pascal Compiler. I toyed with it the other day and it is very much usable! It can create binaries for other platforms such as OS/2 too. It does require some porting though, because not all APIs are the same.

Jonas B.
Thursday, April 01, 2004

Get all of the business logic out of the GUI first.  If you have a core library of model objects written in C, C++, or Java (or Python or Perl or Objective C...) this can be compiled as is on both Windows and Linux.  Do this on the existing code base, without changing any functionality, and making sure it still conforms to your test suite.

This is a good idea anyways, and hopefully you've already done this.  Then work on porting the GUI, line for line, like Joel said.

I'm also surprised that Joel didn't mention what FogCreek did to port FoGBuGzz.  They wrote a compiler that took the ASP version as input and emitted PHP (I think...) as output.

This, of course, takes a certain level of expertise, but could get potentially get you fairly far along in the line for line translation.

Jim Rankin
Thursday, April 01, 2004

>Get all of the business logic out of the GUI first.

I have worked at two companies that were unable to port from text mode to windows because the biz logic was so tangled in the (non-G) UI that it nobody was willing to spend the money to dissect the two apart, and the products, although good, fell into obsolescence.

Text mode is viable, really it is!
Thursday, April 01, 2004

One extra comment: You CAN'T re-write. It is very unlikely that you actually have a specification of sufficient detail to let you write another piece of software that does the same thing. Line-by-line translation is definitely the way to go if you have to switch languages.

And whatever you do, DO NOT allow ANYONE to add features while you port. I'm presently one of the sorry sods trying to finish a 'port' from an embedded system (custom OS) to Linux. Sadly, along with the port a whole host of features were added, and the port was undertaken with NO up-front planning (The initial port was hacked up so a contractor could work on his favorite platform instead of the actual hardware). In the 2 years I've been on the project (which was already a year or so late when I got here!), I've come the inescapable conclusion that we'd have been FAR better off to have not ported at all, but rather to have written a software simulation of the old hardware, then (after first release) slowly refactored pieces out of the simulator onto Linux.

Make sure you have a good handle on ALL the language/OS/Library features that you use. Know what they do, then properly plan for their replacement on the other side. If you don't, you'll get bitten.

Michael Kohne
Friday, April 02, 2004

All excellent comments.  It is critical not to add new functionality during the port.  It is also extremely difficult to keep new functionality out of the port, unless you have support at the highest levels -- otherwise, the urge in the company to 'add value' to the port can't be resisted.

Also -- be aware that if your original code was Fortran, say, and your new code is 'C', that you'll wind up with very Fortan looking 'C' code.  Lots of 'globals' from Fortran 'Common' blocks, for instance.    Pascal strings to 'C' strings is another example.  (You may have to write a Pascal-ish String Library in 'C' to handle this).

Each language has its own built in assumptions and infrastructures which work well with that language.  These may or may not translate well to the new language.  My point is, you may have to relax your style guide during the porting process.  You may also need someone VERY knowledgable in the OLD language's assumptions and infrastructure.

You goal, as Joel has pointed out, is a working piece of code when you're done.  If you WANT to refactor AFTER you have a working version of the code in the new language/environment, do so AFTER you have a working version.

AllanL5
Friday, April 02, 2004

I'm intrigued. Is it really so important to do line-by-line as opposed to taking a bit more course grained approach.
Would it be guaranteed disaster to go e.g. to the function/method level? What about higher up? Class or ADT level? Module?

Just me (Sir to you)
Friday, April 02, 2004

Let me give an example. I'm converting some old database access code in Visual Basic from big-hairy-mess to nice-clean-entity classes. At one point the old code to load a field from a database looked like this:

If IsNull(rs!field) Then
  s = ""
Else
  s = rs!field
End If

I wasn't paying attention to the old code, thinking "how hard could it be to load a string from the database," so my ported code looked like this:

  s = rs!field

Of course that would fail if the column was null. The question is, how could the column be null? I didn't think it would be possible. I think what happened is that certain databases that were upgraded from earlier versions of CityDesk that were missing this field got Null instead of a blank string, and when we had originally rolled out the code without the IsNull check, it had failed for a few people who had gone through that particular conversion process.

So ...  if I had looked at the old code while writing the new code, and BLINDLY translated, I would have had working code. But by allowing myself even a tiny bit of flexibility in the port, thinking I knew more than the younger Joel who wrote that code and then fixed it originally, I lost the knowledge that was embedded in the bug fix. If I hadn't noticed it in time this would have shipped, and the bug would have arisen again in the field, and I would have had to research and fix it again, probably at the cost of several hours.

All production code is made up of a little bit of clean logic and a TON of bug fixes. 90% of the work in a shrinkwrap application goes into the bug fixes... getting what seems like it should work to actually work. And if you don't port line by line, you're losing this 90%.

Joel Spolsky
Fog Creek Software
Friday, April 02, 2004

Does nobody comment their bug fixes?

Part of the clarity of code is the reason it's done a certain way; that's a prime use for comments.

// No, I don't usually comment mine either...

Eponymous
Friday, April 02, 2004

Well, this whole argument that you never ever throw code away, is probably not a absolute things.

- if the project is not very big, then rewriting stuff may be an option.
- often the experience you gained in writing the first version may help  to do the same thing better much better, the second time around.

- major transormations of the code, like separating GUI from logic layer may be necessary, but those 'refactorings' may amount to a larger changes, that is it may look more like a  rewrite.

Michael Moser
Saturday, April 03, 2004

"if the project is not very big, then rewriting stuff may be an option"

I disagree with you on this point.  If a project is small but valuable, then it should not be a candidate for a rewrite.

Joel's point, as I understand it, is not that rewriting is too time-consuming to undertake.  It's just lossy, period.  Your code bleeds value when you rewrite.

So what does size have to do with anything, if rewriting a small-but-useful project means it's worth far less afterwards?  And the ability to get the lost value back cheaply likely just isn't there?  Remember, it's not the loss of lines of code that makes rewriting dangerous, its the loss of _knowledge_ that was encapsulated in that code--much of which came from real experience with real customers (bug reports, feedback, etc), not just blindly banging out code.

It's just as stupid to pay $1 for $0.50, as it is to pay $1,000,000 for $500,000.  Maybe the former hurts less, but it's still asinine.

smkr4
Saturday, April 03, 2004

Gee Joel, you're not a very smart programmer then.

Any half-decent programmer knows that code comments are vital if you write anything more than a 100-line program. That saves you the trouble of looking at the code 6 months from now and thinking, "hmmm... why did I do that?"

Karl Max
Sunday, April 04, 2004

Yes, if I was smarter, there would be a comment, but if I wasn't even looking at the old code, I would never have seen the comment, so it wouldn't have helped. Comments are useful if you are copying the code line by line, but they're not going to help if you decide to throw it away and start over.

Joel Spolsky
Fog Creek Software
Monday, April 05, 2004

Seems the really interesting decision is this :

1) port line-by-line, then refactor

2) refactor old code-base, on old-system, (keeping it working!) but with some architectural decisions that are looking towards the new platform, then port line by line

Anyone got any thoughts on which of these ways you should go?

phil jones
Monday, April 05, 2004

'Refactor on the old system' -- why?  You don't want the old system. I assume you can't support the old system, which is why you want to port to the new system.

And one of the key ideas here is the new system will support the old system's constructs, but in a clunky way.  'C' that looks like Fortran, for instance.  So, the refactoring (and only if it is really needed) is to attempt to remove some of the clunky-ness.  This should result in cleaner development in the future.

If you port first, you now have a useful (if clunky) program.  I suppose you could refactor first (make 'C' looking Fortran, for instance) and then port -- but first of all your refactoring may break the code. 

Second, the refactoring is in fact an optional step. 

Third, it's much easier to refactor in the new environment, where you know what looks clunky, than to 'predict' what is going to be clunky and refactor it before you get there.

AllanL5
Tuesday, April 06, 2004

One of my first jobs was porting old clipper code to C++.  I tried a couple different approaches, but the method that worked best for me was line-by-line. 

  
Tuesday, April 06, 2004

I hate that NULL field thing, I know it indicates the value has not been set, but 99.9% of time a default value like "", 0, current date/time is just fine.

You can set table options in Access so that fields can never be NULL when the record is recreated. This should really be the default behavior because it saves you all that NULL checking.

If that's not done, for strings, it's easier to do val="" & rs!field.

B.Y.
Wednesday, April 07, 2004

If 99% of the time you want to default it, then use SQL's COALESCE() function or write a simple DefaultNull() function.  Of course in VB, you'll need a DefaultNullInt() and DefaultNullString().  In C++ create a nullable<> template that does the same null enabled math in your application.

Creating a function that shrinks 3 or 4 line idioms down to one is extremely powerful.  It saves you that little bit of frustration hundreds of little places around the application.  Plus it makes your intent that much clearer.

Tito
Thursday, April 08, 2004

"If that's not done, for strings, it's easier to do val="" & rs!field. "

A better solution is to handle NULLs in the SQL Query using IsNull in SQL Server (remember that the IsNull function in SQL Server is not the same as the one in Jet) and let the database do the work which will be both faster and more readable.

Wunderkind
Sunday, April 11, 2004

*  Recent Topics

*  Fog Creek Home