Beware the momentum of prototype code

| 4 Comments

A while back Chris Baus wrote a little rant about prototypes. I started to write a piece that defended prototypes when correctly used and ran out of steam; or, more to the point, couldn't really say very much except they're OK except when they're not, try not to get to the point where they're not.

Having just recently been bitten by a prototype that was trying to rise above its station, I now have more to say.

Chris' point about how he has finally killed off some legacy code and how he wished he'd never written this code as a prototype is a valid one; it's scarily easy to let prototype code develop too much momentum, and once it does it's difficult to kill. His view is, never do prototypes. I can understand where he's coming from. It's often far too easy to throw some code together to prove a point and then find that some guy in sales has sold it and you have a bunch of customer changes to put into it and nobody will listen when you say that it's a pile and it should be rewritten. You're lumbered maintaining a pile of unstable crap that never should have been allowed out of the lab. Been there, done that, burnt the tee shirt. But, I still think he's being a little harsh here. The problem is not the prototype itself, or the process that you go through to get to proof of concept, it's the fact that it's good enough for someone to mistake for something that's good enough to do useful work. My advice is a slightly tempered version of Chris' suggestion; do prototypes but never do them that well. Never make them complete enough that they can gain the kind of momentum that makes them have to kill off. Easy enough said...

I find that I have two main styles of programming. The first is the controlled, mostly test driven, quality focused coding that I do when I know where I'm going and when I know that I want to use the code for something serious. I operate in this mode most of the time. Even on personal projects, I build as if I want to go on using what I'm building. This has served me well over the years and I now have a huge amount of reusable code that helps me kick start projects. Not only is there a high chance that I've coded a solution to a problem before, there's a high chance that the solution is of high quality and applicable to the problem at hand. Even stupid things like writing some code to download the GPS tracks from my Christmas present (just so that I could escape and write code and be able to say I wasn't "working" but was just doing some "recreational programming") is done in such a way that if I even need it, what I have is solid and integrates with the rest of my code.

The second style is the one I use when I don't know if the place I'm going is somewhere that I can get to, or even if it's somewhere that I want to be. This is my prototype code. When I'm operating in this way I want to get from A to B as quickly as possible so that I can determine if B is actually what I think it is and if we should really be aiming for it. Often I'll have several B's and I'll blaze a trail towards one, decide that it's not valid and then head towards the next, rinse, repeat. Once I get a feel that one of the destinations is valid I stop. Sometimes it's hard to stop. Sometimes I don't stop quite soon enough. Once I've stopped I start the journey again, this time in my normal style of coding. The originally blazed trail serves as a rough guide to the direction I'm heading but this time I'm looking for solid and reliable code all the way.

At least I try to work like this. Sometimes it's too easy to forget that you're at the end of a trail that was quickly hacked through mostly uncharted terrain. You continue to forge ahead, and, far too late, realise that the trail you blazed is falling apart and closing up as the uncharted lands reclaim what is theirs.

Enough already; my problems with prototype code always seem to start when I put too much of it together. For example, a while back I decided to have a bit of a play around with finding out what I'd like the GUI elements for my debug tools to look like. I developed a few pieces of prototype code that were kept in isolation. I built a couple of dialogs that could display my debug data and built a simple "dialog with a big button" test harness that ran them and fed some pre-canned data into them to display. This worked reasonably well. I got a feel for what I wanted to do in the GUI and didn't tie myself to anything. Later I wanted to know how I would control the application that was being (for want of a better word) debugged. I quickly threw together a simple GUI program that presented the user with a way of controlling what was going on and integrated with my nice, solid, reliable debug tool code. Up until this point the debug tools had all had the same, boring, command line interface.

Unfortunately the GUI worked quite well. I wanted to see what my other dialogs looked like in use with real data, so I integrated them and suddenly the prototype started looking like a real app. A pretty limited real app with a poorly coded GUI but, near enough that if you squinted just right it could be dangerous.

Around that time I got frustrated with the GUI, as is pretty usual for me, and given that I'd proved my points I ticked "prototype GUI" off my list of things to do and picked something interesting looking. The interesting looking thing was to deal with CLR processes in a controlled way. Initially my debug tools used CreateProcess() to start the process under test. As I noted back in September, that was less than ideal (read "broken"). It seems that CreateProcess() doesn't start CLR processes in a suspended state when you ask it to and this was a problem for me. Anyway, I switched to using the Win32 Debug API and wrestled control of CLR processes, ticked that box and rebuilt the GUI prototype to work with the new build of the underlying debugger code. The nice thing was that the interface between the debug engine and the GUI hadn't changed much and integrating the debug engine with the GUI was pretty easy.

I should have spotted the warnings at that point. In fact, there's a hint in the last line of the paragraph above...

The next item on my list was to investigate dealing with debugging child processes. This involved a fair bit of work as up until that point I'd been working with a rather simplistic view of the debugged process. The move from single process to process tree debugging meant moving a pile of code out of the debugger and into a "debug session" class that basically dealt with the debug loop and passed debug events on to the per process classes to deal with. All of this was done in a nicely controlled, unit tested manner and by the end of it I had some nice and robust code in my debug library that handled multiple process debugging. Next on the list was integrating the new debugger functionality with my api hooking and injection code; this lives in a separate library and the tests for that pushed the debug library towards a slightly more generic, pluggable, style that allows me to plug a debug tool specific debugger into the debug session when each new process begins. Once again this was all done with a healthy amount of unit tests, flip-flopping between being test driven and being test supported as the mood took me and the situation demanded. It's actually quite interesting writing unit tests for complicated "units" like debugger interfaces that require dummy processes that can communicate with and be controlled by the test so that the debugger that's under test can be proven to do the right thing; but I digress.

Once all that was sorted it seemed obvious that I should integrate the GUI. That's where the pain began. As I hinted at above, it had been a long time since I'd considered the GUI as prototype code.

When I came to update the prototype to see how my GUI could work with the new engine the prototype GUI code had developed too much momentum. There was a large body of fairly crap code whose original purpose had been to prove a point quickly but had now become something that I felt I had to "integrate". The integration work was slow, not surprisingly, the large weight of shoddy code was hard to change and wanted to keep moving in the same direction (towards Big Ball of Mud status). The relatively 'agile' and flexible code that made up the engine was easier to flex and change than the GUI yet it was the GUI that should have been being changed. Since I viewed the prototype as something that had value, I sought to preserve that value by flexing the engine to fit with the prototype... Bad move.

I had a few painful days of slow progress. Whilst the engine was more flexible it was supported by a nice set of tests which helped point out when I was flexing it a little too much. As for the prototype code, well, that wasn't supported by tests and anyway it was nasty prototype code that was fragile due to insufficient error handling, etc. Eventually I stopped and left it all alone for a few days. When I returned I threw away the compromises that I'd been making to the engine and reverted to the last stable design. The correct approach, at least for me, is to build the interface that A GUI requires of the engine rather than the interface that the prototype GUI that will be thrown away requires. In fact, by making this decision, this prototype has been thrown away.

It always was my belief, but this last episode has helped to confirm it; prototype code is fine as long as it prototypes one thing. As soon as you start lumping it together, you can start to mistake it for something it isn't. You forget it's throwaway because it doesn't look throwaway anymore. It looks and feels like a baby app and so you start to give it value. Keep your prototype code small and special purpose. That doesn't mean that you can't prototype lots of the system all at once, just fight the lure of plugging this prototype with that one because it would be cool to see feature A work with feature B. Use your imagination and keep the prototype code from gaining momentum as it becomes a big ball of mud that just wants to keep on rolling...

4 Comments

One thing I've found useful with prototyping is to use a different language...preferably one you *can't* use in production.

For example - I was putting together some stuff for managing a set of graphs & pages (it's for test equipment for embedded systems software). Prototyped it in Python to get the details correct & rewrote in C++ when I was happy with it. Have to say, I did miss Python list comprehensions when I was rewriting it :-)

Stuart

That's a very good point actually. Though in this case I was actually interested in working out if I needed to purchase any display components or if I could get away with adjusting some stuff from the web. It probably would have been better to glue the various parts of the GUI together using VB to get the mock up working rather than doing it in C++ and wiring it up to the real debug engine...

Ok I took an extreme position there because of the problems I had patching together prototype code. It difficult for non-technical managers to understand the difference between a one off hack and releasable project.

Chris

I agree, my point is that sometimes it's also difficult for the techies who put the prototype together to keep it clear in their head that it is throwaway. Hence my suggestion to try and keep the pieces separate and prototype features rather than the whole app.

Leave a comment