An enormous claim, I know. But this is not about processes, tools or working conditions. This is about something quite different.
Shrew is progressing, it now sports an s-expr to XML evaluator; I’m reading RESTful Web Services to gain a better idea of how it should expose resources. And I’m also working through The Seasoned Schemer. And therein lies the most interesting aspect to Shrew. I am a reasonably experienced, quite competent polyglot software engineer, but learning Scheme has forever changed how I think about programming. And through example crystallised the ultimate development environment that I have been drifting towards.
When I’m working on Shrew, my editor looks something like this:
In the top-left is the module I am currently working on: writing, expanding or fixing - as I’ll try to show there isn’t really any difference between those three. In the top-right is a scratch file that contains a bunch of ad-hoc tests for the module I’m working on: nothing structured, just calls of the functions that I’m writing. Across the bottom is the output from a Scheme process running in the background.
Before I go on there’s one detail of Scheme I should explain. To write a new function you call a built-in function called
define, passing the name of the new function and its body.
define is smart enough to simply replace the body if a function of that name already exists.
It sounds like a pretty simple development environment: a plain text editor with three windows on screen. Why so special?
I write a function - not a test, sample or prototype, but the real code I’m planning to commit - I jump to the end of the
define and run a command in my editor to evaluate it. That function is then inserted into the running Scheme process and available to be used by anything else that is run in that Scheme process. Or, I’m immediately informed of a syntax error in my code.
I switch over to the scratch file and write some code to call the function I just implemented: typically just one expression, but it can be as many as I need. I evaluate that new code. And immediately see the output in the window at the bottom; the window reflecting the running state of the background Scheme process.
And of course, there’s a bug in my function. I switch back to the window containing the module, fix the bug and re-evaluate. I switch back to the test code, re-evaluate that, and see that my change has fixed the bug.
Elapsed time from writing the function through debugging and verifying the fix: 45 seconds.
Instead of having to write a complete library, compile it, write a test harness, compile that and link it to the library and only then run the code to see if it works, I have a Scheme process running in the background that I can just keep adding code to. New code, or code to replace existing code. And at any point I can execute any sub-part of that process and immediately see the output. No delays, no pauses, no backtracking to find which line of code is wrong.
Scheme could be regarded as a fairly direct implementation of a theoretical model of computation: the lambda calculus. Most texts that teach Scheme emphasise this; they encourage you to think of your programs in terms of this theory, in quite some detail. That may sound fairly esoteric, but once you’ve spent sometime working in this environment you reach this unique state. You are inside your program, reaching around moving code as fast as you can think. There is essentially nothing between your thought and code: no defining boilerplate, no compiling, no creating test harnesses, no waiting for test runs to complete. Your solution simply unfolds before you.
But that’s not to say your code is of lower quality. In fact, because you’re concentrating more fully, with no distractions and the flexibility to easily push your code in any way you want, the code is of much higher quality. There’s no idle thought ‘I should test that’ which is forgotten in the edit-compile-run-debug cycle: think it, try it. This is flow as Peopleware could only dream.
And once you break out of this magical flow, you’re left with complete code; code you lived and breathed for a few hours, code you understand deeply and will have a hard time forgetting. Plus, a comprehensive set of tests to commit alongside.