Always start with working code!

Sounds dumb right? How can you always start with working code? If you are just starting a new project, you have no code, so you can't start with working code. Wrong. It's easy. Start1 by typing as much as code as you know will work. Eg. Many file processing and filter type scripts I will start with:

#! perl -slw use strict; while( <> ) { print; }

1 You have to start somewhere. If you type just while( <> ) { and tried to compile it, it would fail. So don't do that:)

I haven't tried to compile or run that, but I am fairly confident, that it will run without errors on my machine! Of course, the shebang line means that it will likely fail on other setups, but that is true for many variations of shebang line, even between different installations of the same OS.

Start small

The point is to start small and then add stuff in small steps. Don't move on until you have made the current step work. As you gain experience, you will be able to type more each step with a reasonable degree of confidence that it will work, but don't get over confident. Any time you type a line of code, and you are not sure if it is correct, stop. Try it.

Trying it can mean doing several things. Often a good first pass is to do perl -c yourscript and see whether that produces any errors. If you are unsure of the syntax of something, typing perl -de1 will give you a debugger session in which you can type simple one liners and have the syntax checked immediately. I've got a really simple script which reads from the keyboard and evals what I type which I use all the time. It has a few extras, like it imports several utility modules like Data::Dumper, List::Utils and my own personal utils module. It also is set up so that I can end a line with \ and it accumulates the lines until it gets one without a \ before evaling them which is useful. I often thought of trying to clean it up a bit to make it more useful, but I run it many times every day, and only get around to adding to it when I need to. It serves my purpose very well. It's only about 10 lines long.

If your lucky enough to be working on a fairly simple piece of code like above, then you can try it on your test data. You do have a file of test data don't you?

Test data

If you don't STOP. Start another editor session and create one. If your editor has macro ability, this maybe all you need to produce a reasonable facsimile of your data. If not, you may need to write another short script to generate it. rand is great for this. It doesn't need to be too exact, but just close enough to exercise your program. And it needn't be too big, just big enough to contain one or two examples of data that will exercise each path of your code. Even this isn't necessary for your first pass, you can always modify and improve your test data as you go, making it more sophisticated as you code each new path of your program.

Save often

Each time you test your program and it works. Save it. That is, if your editor doesn't handle it for you, then make a backup. I tend to keep at least 3 ordered copies of each file of any serious project. If your editor doesn't do this for you, make yourself a small batch, shell or perl script to do it for you. If the way you work is that you never leave your editor, or close your file when developing, then create a macro or shortcut or whatever that allows to backup your file with the latest set of changes when you choose to, Ie., not every time you save the file before testing it. Yes, in most decent code editors you can always use undo to move backwards, but

  1. You may not remember the right place to go back to.
  2. There is always the temptation to think that you knew what change you made that broke the code and only back-step part way. Then you think you know what you did wrong last time, and so you make knew changes. They don't work.

    So now you have a hybrid situation. Maybe some of your new stuff does work, but it comes after some other stuff that doesn't. Now your in the situation that you have stuff in your undo buffer that you want to keep, but it comes after stuff that you want to undo. Plus you can't remember how far back you need to go to get to the point where it was working.

Have a way of committing a backup that is independent of the normal save function/ key/ command.... And use it! But only when you have working code.

Throw it away!

When you add a section of code and it just point blank refuses to work. Bin it! Retrieve the last working backup and start again. Do not be afraid to throw away a piece of code you just spent the last 4 hours working on. Don't think it was a waste, it wasn't. Often, your second pass comes out much better than your first. You now have the experience of doing it the first time and when your writing the second, this will be fresh in your mind. The good bits will come back naturally. The bad bits will tend to stand out as such. Don't try keeping the non-working code and modifying it. This invariably leads you into just repeating the same mistakes. If you have more than 4 hours worth of code since you made your last backup of a working version, you are waiting too long between testing! If you have the discipline to throw away and start again, you'll rapidly cure yourself of going too far before testing:) Personally, I rarely spend more than 20 minutes or half an hour coding without performing a test, but different folks, different strokes.

Try it! It works.

It's not only for small projects

That's all well and good I hear (somebody) saying, if your program is a silly little one file script with a single source of data, but it wouldn't work for my project because it consists of many modules (classes etc.), and it is impossible to run one module in isolation of the whole suite. And anyway, the module I am working on has dependencies upon other modules that I haven't coded yet, so I cannot test until I have coded them also. Wrong!

Design first

When coding a module, you should have a fairly clear idea of the interface to that module. That means you should know, before you start coding pretty clearly what functions, procedures or methods will be available for use by callers of the module. You should also have a fairly clear idea of the parameters they take and the sort of results you should expect. So code a "caller" that uses that interface. In perl modules I tend to code this as a "main' program within the same file.

package doit; sub new{ return bless {}, $_[0]; } sub enumerate{ my( $aref ) = @_; return @$_; } .... return 1 if caller(); package main; use strict; my $doit = doit->new(); my @data = 1..10; print $doit->enumerate( \@data );

Dummy up

In this way, I can just run the module to test the code. It makes writing a "working" module easy. Just dummy up all of the interface calls and have them return something vaguely sensible, and write code in the main package to call those dummy functions. As the code in the module gets more sophisticated, so the test code can be made so. Or maybe do it the other way around. As you increase the reality and complexity of the test code, so you update the module code to suit. I prefer the former, but it's not a hard and fast thing, just a preference.

Not only does this answer the "can't be run in isolation" problem, it also answers the dependencies problem. If your module has dependencies, it takes very little time to code that module using placeholders for the real code that don't even inspect their inputs, but simple return a hard coded (or even randomly chosen) "correct" answer when called. This allows you to get on with coding and testing your current module. It also acts a "working starting place" when you get around to coding the dependant module for real. Once you have completed (to some definition) the functionality of your current module, you have a ready made test script for the dependency module.

Finish well or throw it away

Finally. I always try to finish, for the day, night, week etc, with a working program. If I haven't, I don't throw what I have away there and then, but I do seriously consider doing so when I start again the next session. If I cannot see the problem within about 20 minutes (maybe a little longer if I've just woken and am caffeine deficient:), then I will save the current stuff under a different name and revert to the previous working version. Almost always, I will never refer back to the renamed, non-working version and it ends up in the bit bucket, but just occasionally, I get to a point when re-coding where I remember having found a neat solution to some bit, but can't remember the details. Then I refer back to that bit of the code. I NEVER revert to the non-working version in it's entirety!

This was prompted by various things, mostly my having spent too long and become really frustrated trying to get something to work before realising I should have followed my own advice above and binned the non-working code and started again. When I did, I had the new version coded in about a quarter of the time I had already spent trying to get the previous version to work.

Well, there it is FWIW. Rather longer than I intended, but maybe it will help someone.

Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
If I understand your problem, I can solve it! Of course, the same can be said for you.