Project number #n is done, it’s time to work on project number #(n+1). As you will keep the same technology but make a different game, you are now facing two options:
- Copy the whole project #1 and start ripping out the parts you don’t need anymore.
- Start everything from scratch.
If you think both options are terrible, that’s because they are.
Ripping out stuff is a lot easier said than done, as you remove one part the whole dependency chain falls apart. So you will spend a large amount of time fixing the project even before it started, and chances are extremely high that some useless stuff will remain in there anyway. Further down the line, you’ll wonder what the “SpawnUnicorn” is doing in your top-down space shooter, and eventually realize that the new project built new dependencies on it.
On the other hand, starting from scratch is seducing. There comes the chance to avoid the shortcomings of the previous project setup and get it right from the start! That won’t be much of a problem because you probably know and remember exactly what had to be fixed in project #n during the last few years, right? Wrong, and you know where this is going: you will screw up somewhere, and you will have to fix the damn thing again, but with all the added complexity of the second-system effect.
But you know there is a third option: a project template. A small sample that doesn’t do much more than rendering a cube with a hello world texture, but with all the right stuff setup in the right place. Of course it will take some time to get right, but it only has to be done once and it will grow into a huge time saver in the long run. You could even turn it into a wizard for your favourite IDE, and whatnot.
Now did you notice we just combined the drawbacks of the first two approaches?
A project template is just like documentation: a great idea that can quickly turn into living hell if left unattended. Just like outdated documentation, an outdated template might lure you into the dreaded land of deprecation. Even with a lot of goodwill, if documentation and project templates are not part of your core business (so if you are not a middleware provider), you won’t manage to keep them up to date (and even then I am not so sure about it, but let’s not go there).
So what? Well I am still looking for the right way of dealing with this, but I have a few suggestions. They might be rather hard to put into practice on larger projects, but at least they might give pointers.
The basic idea is: prepare for the worse, your previous project is the most reliable configuration you have, you’ll have to deal with it. So I try to keep the following points in mind when working on project #n to prepare for project #(n+1):
- Aggressively delete all code that serves no purpose anymore as you go, code that is not called is a liability. And don’t comment it out, if you ever need it back, it will still be there in source control. Make it so that you can actually remove stuff without breaking the whole thing. And removing is not about having code that is not called, it really is getting rid of it (YAGNI).
- Avoid dependencies on the computer itself, use the project files instead. Use relative paths, use build command macros (read Paul Laska’s wonderful article here: User Defined Build Command Macros in MSVS 2010), standardize your naming, eventually use symbolic links (but be careful, they can get confusing).
- If you can store cross-project configuration outside the project itself, do it. Properties sheets in MSVC are great, use them and regularly push everything not project-specific in there. They also make it convenient to share settings amongst build configurations and give you a bird’s eye view.
- Reduce the amount of libraries you use. Sure, you should avoid reinventing the wheel, but when a reasonable amount of ad-hoc code can replace a large dependency, it might be worth considering. Also, unity builds work just fine for quickly including small libraries.
- Avoid excessive flexibility in configuration, group settings together. If something depends on something else make a naming convention so you can deduce one from the other. Don’t put all your symbol definitions in the project configuration, put them in headers where possible.
- Don’t make too many build targets, they will get broken at some point. The less you have, the less you have to fix. Only keep the ones you really need on a daily basis, and ensure that your build server covers them all.
- Be very careful with stuff that happens before main, automatic initialization seems convenient but will you still remember what triggers when and where a few years from now?
- When doing something in a way you suspect to be sub-optimal, make sure you leave a comment. And make it meaningful, it might be obvious at the time you write it but in a few months your “// lol hack” won’t help.