When we started thinking about it, Jonathan threw these contraints at me:
- The code must be peer- or manager-reviewed
- We need a separate QA departement, with testers playing with an already reviewed version (no playtests of the development branches)
- Application components must be isolated enough so that we can develop them at different paces and submit versions to be tested at different times, without ending with a half-coded module in QA's hands
- Stable versions must be released on a regular basis, once per week for this project
- Automated unit tests would be really nice
- A build server reporting code breaks and running tests could be really cool too
And keeping it lightweight is not only about style... If we fail that part, then we put the project in danger for two main reasons:
- Obviously, if the processes are too heavy, people will lose too much time applying them and the project will quickly get very late.
- If the workflow is too complex, everybody will eventually get lost in it, work around it, thus making it completely inefficient.
Don't-think-about-it philosophy
In order to find an appropriate solution to our problem, we can start by defining a general working philosophy. Because we need the team to realize a huge set of things in a short amount of time by following relatively hard constraints, the more burden we can remove, the better. Part of removing the burden is obtained by applying what I call the "dont-think-about-it" philosophy.
That's why you can buy a golden fish without bringing water with you to the store.
Really.
Because it's annoying (and risky for the car) to carry a water bag, because you wouldn't know what kind of water (salted?) to bring, because maybe you didn't even plan to buy a fish at this moment, etc.
But while marketing professionals seems to have figured that out for a very long time, computer science looks a bit outdated on this.
The point is that when you have to think about a lot of things at the same time, the chances that you get something wrong increases very, very quickly. In a similar spirit, when you have to check many things before doing something, it is likely that you will forget one or two and then realize that this was wrong.
Thus, when designing a workflow, we should strive towards two main goals. First, always reduce the likeliness of something wrong done, by reducing choices and checks a developer has to do in everyday operations. And second, we should always design the system by taking into account that mistakes and errors will happen.
If this sounds familiar, it is normal. That can almost entirely be found in any good UI design course. The fact is that things doesn't have to become overly complicated when we get to developer side.
In our project, this state of mind translates to as much sentences as possible looking that way:
- Developers should not be scared of pushing/committing something on the central server. Never.
- Finding which version of the game a bug is related to should not require any research in the repositori(es). When a bug pops out, everybody should know on which version it has been found.
- Nothing in the compilation process should have to be handled manually.
- No special action (to be remembered) should be taken at each commit/push, including triggering the build server for a new build.
- There should not be any repetitive and error-prone work (e.g copy/paste/replace). Obviously this kind of thing can be hidden in scripts or small IDE tools, but it's generally much better not to have it altogether where possible.
- etc.
The tool side: trying bottom-up
Because whatever solution we choose we will have to implement it on existing tools, here is a small review of what tools we need, and what alternatives we considered.
The code must live on one or more centralized repositories. It might seem obvious at first sight, but DVCS like git or mercurial allow developers to push/pull directly from someone else's repository, which would allow server-less source control. But in our case, the team is exploded between numerous European countries, and we cannot expect a developer machine to have the same disponibility level as in a company local network.
Thus we must make a choice between the hosting possibilities:
- Managing our own server. This is the most flexible option, but it will inevitably take a lot of time to set up and administer everything.
- Using a free online service, such as Google Code, GitHub or BitBucket, and add missing features (such as a build server) on our side.
For many reasons, we chose the latter solution. These reasons include administration burden, server reliability and backups, and more generally the fact that the free online offers are enough for what we need. We chose google code because most of the team is familiar with it and with other google tools (e.g docs), and because we wanted to give Mercurial a try on this project, to set up a real DVCS workflow (see below).
To store the different game versions (such as development, testing, releases...), we need a powerful version control system, supporting a lot of branching and merging. This led naturally to Mercurial on Google Code, which allows multiple repositories for free. The repository architecture is made with the following goals in mind:
- Stable releases should be separated on their own repository
- QA testing and bug fixes are done on their own repository
- Feature development is done in several repositories, one per architecture component
- Features can be integrated separately into the testing repository
- Repositories have to communicate. For instance, bug fixes done in the QA repositories must be integrated in the development branches. Symmetrically, reviewed components must be pushed to the QA for testing.
Choosing Google Code as a back-end service also helps a lot with the integration of other services we need. Code review can be done directly in the code, on a line-by-line basis, and issue tracking is tightly integrated in the web UI and with commit messages. This is a great advantage because it means developers don't have to go elsewhere (i.e another service, another URL) to do these things. The wiki is equally integrated, and will allow easy project documentation.
As the game is cross-platform and we don't want to lock the developers in only one-tool (i.e a specific IDE), the build system is based on the powerful SCons, which is easily callable from any IDE project, and allows for great build flexibility and cross-platformness. As the build is a complicated topic, another article will be written to explain how our build system was thought and implemented over SCons. The main design points are the following ones:
- Cross-platform and cross-compiler. Users may chooses to reduce compiler choices, but the toolchain must support all of them, theoretically.
- Component concept. Components are something between an IDE project and a package-config file, generalized.
- Explicit component dependencies. This will allow for component dependency graph generation, avoid duplicated configurations and kill dependency cycles.
- Powerful configuration. Everything is Python.
We also make use of a build server which is automatically triggered by Google Code, through Post Commit WebHooks. This allows us to be almost instantly notified if we break the build or some unit tests.
Finally, unit testing is kept simple and flexible. That means we allow both automated and non-automated tests to be written, and that we developed a very simple library to help with test development. Actually, that library is just a header with three macros in it, such as CHECK(expression) which will print either a positive or negative result to the output, along with information about file and line where errors happened.
Allowing non-automated tests is also an important point: we don't want developers to waste time trying to fit a simple module test within a complicated yes/no framework, especially when this test is easy to verify but hard to automate (is that texture yellow?).
Everyday life made simple
As stated before, the ultimate goal of this tool combination is to make developer life simpler. Maybe it is not ideal, but I believe we have reached a fair degree of simplification. The build system handles weird compilation details, the build server handles running the tests as often as possible, writing tests is easy, pushing is not scary because the repositories are separated, bugfixes can be instantly imported in development branches...
But obviously, as this workflow is new, we cannot (yet) know what will really work and what will not. More about that after the project has finished and remarks have been collected...
No comments:
Post a Comment