Software Maintenance and Prototype Based Languages

Have you ever been using an Open Source application and noticed something horribly wrong? I have and as a skilled maintenance programmer it really tickles my fix-it bone. I know I could fix it if I wanted to but it's just so much effort. Usually it's only when a bug really annoys the hell out of me that I'll even go to the trouble of downloading the source code (or even finding out where I can download the source code from). In the rare moments that I have taken on the feat of fixing someone else's code I've found myself exercising my most mad maintenance programmer skills and I decided to make a little list.

  1. Identify the problem.
  2. Try to reproduce the problem.
  3. Identify what component has the problem.
  4. Locate the source code for the component.
  5. Figure out how to build the source code you've found.
  6. Ensure that the source code you've found compiles into a component that has the problem you have identified.
  7. Locate where in the source code the problem lies.
  8. Read and understand how the source code produces the problem.
  9. Build a plan for how to change the source code so it doesn't have the problem anymore.
  10. Make sure you've made a backup of the source code somewhere.
  11. Make the necessary changes ensuring you don't break anything else.
  12. Re-compile the component.
  13. Try to reproduce the problem with the new component. If you can, go back to step 8.
  14. Make sure you fixed the problem in the best way possible.
  15. Try to tell someone what you fixed and give them your changes.

Did I mention how much effort this is? What are we going to do about this? The usual solution is simply not to fix other people's code. We can report the bug as best we can, wait around for it to be fixed and then go and grab the next release when it is available. Usually this is a lot less effort but it sort of defeats the purpose of having the source and it really doesn't make good use of my mad maintenance programmer skills (but I suppose it does make use of my mad QA skills).

When people fix their own code they don't need to go through steps 3 through 8, or at least not to the same degree that someone who has never looked at their code before in their life does. In particular, it's steps 7 and 8 that takes the most time (and really flexs the muscles of veteran maintenance programmer). In good object oriented code step 8 is usually shorter than step 7, simply because such code has short methods in concise objects that relate almost directly to the domain objects (which is the terms people think in when they identify problems in software). As such, it's a heck of a lot faster to fix bugs in good object oriented software than it is to fix them in procedural code.

However, that step 7 is still a killer. It takes so long to search through megs of source code to find the particular object that has the problem. Trying to guess the vocabulary of the programmer and look for similar files (or using grep) is the most common practice. That's pretty sad. Ultimately we'd like to be able to get a visual overview of the objects in the system and easily correspond these objects to the kinds of domain objects we might be using to define the problem. What would also be good is if we could narrow down that visual overview to just what is relevant to the current state of the running program. Then one could running the offending software, stop when the problem is manifesting itself and look at what objects are activate at that point in the execution. That way we can quickly narrow down what objects are involved and get on to step 8, actually figuring out what is causing the problem, without having to learn how the whole beast works.

Ironically, steps 12 and 13 take the most time in maintenance programming. This is the dreaded edit-compile-test cycle and it's not getting any shorter. If you use a language like Java over a language like C++ you have some appreciation of how much faster compile times are. On the other hand, startup times are ridiculously long for Java apps compared to native C++ apps (not to start any flame wars).

But ultimately, they're both too damn slow for maintenance work. Almost all problems which involve UI issues will take hours to debug using these two. Why? Because you're spending 90% of you time edit+compiling and 10% testing. By the time you've fixed the first part of the problem you've forgotten about the other 3 parts and you're ready to say "maybe it wasn't so bad after all". Resource editors and UI builders cut down on this a little, but they're only a half-way solution. It's so hard to see what the problem really is if you can't click on a button and get an instant, live, response from the application, just as the user is going to see it.

Prototype based objected oriented languages like Self and Io solve both the edit-compile-test cycle and make it easier to understand the software (steps 12, 13 and 8). When you're using a prototype based language you can edit objects as they're running. There is no separate compilation step, and your program is always running. The software is easier to understand because you're not looking at it from above, you're in there with the objects telling them how they should behave. In a class based object oriented language like Java or C++ you're telling classes how they should tell objects to behave. If, at run time, you decide that an object should have had different behavior or should have been of a different class, too bad. Even if you can edit the class at run time (as some experienced Java programmers do) you're going to have to kill the existing objects of that class and make new ones. This means the time spent planning your solution (step 9) better be spent well or you're going to spend more time in edit-compile-test than you did understanding the software. With a prototype based language you can explore possible solutions at run time until to find one that does everything you want. You're also more likely to find one that satisfies step 14, fixing the problem in the best way possible.

Of course, when you get to the stage where you can effortlessly fix a problem on a running program you may just forget to tell anyone that you did. Worse yet, you might forget to update the source code for the application and the next time you run it you'll have to put up with that problem you've already fixed or fix it again. Thankfully prototype based languages have recognized this problem long ago and provide a solution in the form of persistence. When an object is modified in the exploratory development environment of a prototype based language it is almost always copied to a backing store where it is retrievable at a later date. I say almost because to a certain degree programmers in prototype based languages still cling on to the class/object distinction. In Self the "classes" typically have names that end in the word "traits" (and the object typically don't have any names at all!). So if you're tacking new behavior onto the running object you're going to lose it once the garbage collector comes chomping along. Typically this is bad form anyway, if you're worried about messing up the traits object then do a clone of it and redirect the object's parent* slot to the clone (yes, you can change an object's "class" at run time in prototype based languages) and remember, if you do this in Io you'll have to copy the slots you want to change over to the clone yourself. This is essentially the prototype way of doing step 10, you backup the objects you need to, when you need to.

As prototype based programming languages become fashionable again we may yet see some very interesting integration of revision control systems with exploratory development environments. Prototype based programming is about rapid persistent development and revision control is still stuck in the world of files and source code merging. The ability to track changes in a development environment changes the kinds of things that can be done automatically with revision control, like maintaining history when moving methods from one object to another and other big refactorings. Try doing this with Java files in the best revision control systems around (like Perforce and SVN) and then try merging from another branch.

When it comes to prototype based programming, the best it yet to come.


QuantumG
<< back to my home page