June 30, 2006

...Learn TDD with Codemanship

MOVE!

When I'm illustrating how evolutionary design (e.g., test-driven development + refactoring) works, I find it helpful to use a simple programming language I've invented called MOVE to get the key concepts across. A programming language defines a (usually infinite) set of all possible programs that can be written in that language. The advantage of MOVE is that it is so simple that this infinite solution space can be easily represented in a simple 2D grid.

MOVE is a program that moves a cursor in only two directions along an X-axis and the Y-axis. It has a command called RIGHT which moves the cursor to the right by one square (X = X+1), and it has a command called UP that moves the cursor up by one square (Y = Y+1).

A MOVE program would look like this:

RIGHT
RIGHT
UP
UP
RIGHT

The MOVE interpreter reads programs in such a way that:

RIGHT
UP
UP

and

UP
RIGHT
UP

Would be interpreted as the same MOVE program, because they move the cursor to the same square in the grid. Therefore, each unique MOVE program is defined as a unique X,Y co-ordinate in the MOVE solution space.

Boy, that all sounds swell, doesn't it? But what's it got to do with evolutionary design?

Take test-driven deveopment, for example: In MOVE, we can ask only two things - what is the X co-ordinate, and what is the Y-co-ordinate? So test assertions (contraints on a MOVE program) create restrictions on which rows and columns the cursor is allowed to end up in. It creates a landscape of safe squares - squares that satisfy the constraint - and killer squares - squares that don't satisfy the constraint.



Now, every MOVE program starts with the cursor at 1,1. We might write a test that says that only squares where X > 2 are safe. Suddenly, 1,1 isn't safe any more, and our program breaks the test. We could then write the code needed to move the cursor on to the nearest safe square. Now our program passes the test. So far so good.

Then we might write another test, say Y > 3. Now our program fails one of its tests again, so we write just enough code to get the cursor into a safe square again. And we're back to all tests passing. Phew!

Finally, maybe we write a test that says that X > Y. Yet again, we're in the danger zone and we have a failing test. So we write just enough code to move on to the nearest safe square - and we're back to a green light.

I've found this very useful to help me visualise the mechanics of test-driven development. In a real programming language like Java, there are many, many dimensions in the solution hyperspace that the language defines, making it nigh on impossible to get to grips with what's happening in such straightforward, concrete terms. Restricting it to RIGHT, UP, X and Y just seem to make it that much easier to see the wood for the trees. (Well, I think so, anyway.)

I'm using MOVE to help me visualise other cool things, like design quality, and the refactoring process (imagine imposing non-functional constraints on a MOVE program and then refactoring it to satisfy those constraints - e.g., program length).

I'm also using it to help me explore concepts like productivity. Given a simple language like MOVE, how would you define productivity of a MOVE programmer? I'm still working on it

Posted 14 years, 6 months ago on June 30, 2006