I read an interesting article a little over a week ago, called “”Think by Building / Build to Answer Questions” on hobbygamedev.com. I believe I understood the crux of the author’s point and agreed with his statements in general; however, if I were to task to writing a similar article or passing along the article to novice developer, I might present the following points:
It’s okay to throw away code
I believe this was a key point of the original article. You, as a coder – especially in a small hobby-coding environment – are never going to know ‘exactly’ how everything will work in advance. There’s simply too much complexity to fit into one person’s head at once. In the context of a hobby project (where you don’t have a black-box test team), trying to record all those individual details into a specification first isn’t necessarily worthwhile either: that same time could be spent simply coding all those details!
Another key point of the article was that often is an unhelpful sense that once code is written, it can’t be discarded. As a coder, it’s important to get past that feeling when it is encountered. If you’re at least considering that rewriting a section of code might be a good idea, then that implies there’s a good chance that the code is not satisfying some requirement you now see. Don’t expect all code to be perfect the first time it is written: see the prior paragraph about it being impossible to know all the details in advanced.
The article also makes a good point experimenting with cost is far lower cost than experimenting with bridge building. Remember the low cost and don’t be afraid to discard something that doesn’t work.
In other words, as you code, you learn. As you learn, you find mistakes in your previous thinking (that happens in all forms of learning, not just coding). It’s okay to throw away old code in order to improve the overall code base.
Don’t expect to know all the details in advance, but do use proven, general designs and design patterns
The original article talks about guitarists strumming their guitars, chefs cooking new recipes, and painters painting the canvas as a means to “build.” However, this is only one aspect of how an artists works. Painters most certainly study composition and color theory, chefs visit gourmet restaurants as well as know classical recipes, and guitarists certainly have learn at least a minimal amount about chords before diving too seriously into song writing. I highly doubt the author was in any way denying this, but he did not put much emphasis that all the study and preparation that happens to accompany the more freeform invention stage that takes place while actually participating in the art.
Good code likewise requires preparation (guitarists know the chords, programmers know the programming language) and design (painters know visual composition, programmers know design patterns). Heads down coding will help test designs and refine them. Heads down coding does not however do well as well to invent new concepts and patterns. Heads down coding is good for working out details, but less helpful on working out the big picture.
There’s a definite line between “discovering ideas by building” and “testing ideas by building.” The latter works very well – there’s nothing like actually trying something to prove if it will work, but the former is simply not an efficient means of getting things done. If you think you have a fantastic idea on how to sort a set of numbers – go ahead and code it. See if it works. That’ll expose you to all the details. But if you want to sort a set of numbers, sitting down to code until you “think up” how to do it…well, your time could be better spent by utilizing the work of decades of study by others in how to sort numbers. Be aware of qsort, std::sort<>, bubble sorts, radix sorts, etc. and then test which works best for you.
Of course, “sorting” is an extreme example and in reality it’s a gray line between what is a “detail” and what is a “design” and what amounts to “discovery” versus “testing”; however, I think it’s important to remember that coding in and of itself is far better at refining what you already know rather than expanding the realm of what you know.
Dependencies, dependencies, dependencies
The original article points out that code is cheaper than bridge building materials and while this is correct, it can be painful to rip out a chunk of code in a complex project as it may break everything that depends on it. That’s why there are principles like the separation of interface and implementation. That’s why there concepts like Adapter and Decorator design patterns.
A good design, made in advance, allows the details to change more easily. A good initial design has a symbiotic relationship that actually allows you to “think by building” more easily. So as a general rule, make a component depend on exactly what it needs to depend on to meet its objective and nothing more:
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” — Antoine de Saint-Exupery
Do the same with your code. Use good, clean, proven, minimal designs and make the rest just details.
Keep a sketchbook handy
The crux of this post is a common sense point: keep a balance.
- Plan and prepare in advance using a good, proven initial design. The better the initial interface, the more it will allow you to experiment and refine more easily.
- Experiment and refine actively. It is an instructive process and there’s no more certain way to test out an idea to see if it really will work.
As a practical means of employing this strategy, make sure that every type of code has its place. A painter has large expensive canvases for the final work as well as scraps of discarded paper for doodling on. Both are real parts of his tool set. Code should operate the same way. Have a place to experiment with code as well as a place for the production-level code – and, have a migration path between the two. For example:
- Use git branches for experimental features
- Allow your developers (yourself included) to miss deadlines because an idea didn’t work
- Account for the last bullet point in your project schedule (otherwise, you’re not really allowing it…)
- Ensure your main application supports extensions and plug-ins for new features that might not ship
- Use unit tests to testing new experimental implementations of existing interfaces
- Have a “Labs” area for prototype code
All in all, design and implementation are a balance, so actively account for that in your projects.