Dodgy Coder wrote a post about old school developers achieving a lot with a little. Developers such as Ken Thompson, Joe Armstrong, and Jamie Zawinski essentially eschew modern development practices and ideologies — Agile, Scrum, XP, TDD — and instead rely on thinking and print statements.
Does this really surprise anyone?
Mark Twain famously quipped he never let schooling interfere with his education. Likewise I don’t allow development methodologies to interfere with my programming.
I tend to build software both top-down and bottom-up simultaneously. I code to the middle. I first start top-down so I can establish the landscape, determine the main actors, where the core functionality lies and establish a rough sense of the required underlying data model, software services and interfaces required to support this functionality. Then I switch to the bottom-up mode to work on the data model and those software services and interfaces to ensure it’s reasonable and will perform as expected. Sometimes changes are needed and so I need to rinse and repeat until I’m fairly satisfied I have a workable solution.
Now I begin the activity most non-developers think of as development. I begin fleshing everything out and connecting it all together. I work on the hard, read risky, items first. That accomplishes two things:
- Assurance my solution is technically correct and I don’t need to go back to the drawing board.
- Validation of project schedule. If I need to go back to the drawing board then the earlier I do so the better.
So now that I’m coding how do I do it? Mainly by using print statements. Now that I’m mainly doing Java development I use log4j debug statements, but it’s the same thing. Log4j simply gives me the convenience of turning off my print statements in production without having to modify my code.
Of course I test my software, and since regression testing is essential for ongoing maintenance I capture these tests as unit tests. I have a two-fold strategy for unit testing:
- Coarse-grained functionality testing
- Fine-grained testing of risky implementation items
I’ve come to find this strategy both minimizes the amount of test cases I need to write and also minimizes the risk of bugs slipping into production.
So how is this maintenance driven development?
When I find bugs using my coarse-grained functionality tests I capture it using a newly-created fine-grained test. This way I can repeat the bug, isolate it, and work to resolve it. I proceed in this manner until all coarse-grained tests have passed.
Likewise when bugs are found in production the first step to resolving it is to create a new fine-grained test that repeats and isolates the bug. Then work to resolve it.
The bottom line is I don’t create a lot of fine-grained tests until I need them. When I need them it’s going to be for code that has been problematic at some point in the past, and now that I have a test for it I can ensure it won’t be problematic in the future.