In this chapter, we discuss two important questions in development and provide a single answer for both. They turn out to be fundamental not only to the logical structure of the code development process but also to the production methodology.
Here are the questions:
The worst thing in the world as far as development is concerned is to be writing system-critical code towards the end of a project. Yet this is such a common occurrence you would think someone would have spotted it and put a stop to it. Not only will (and does) it induce huge amounts of stress in the team, it is absolutely guaranteed to introduce all sorts of problems in other systems that were considered stable.
Ideally, we would like to pick an order to perform tasks in that does not lead to this horror. Ideally, we would like to be able to know in advance which are the tasks and systems that we need to work on first and which are those that can wait a while. If we cannot attain this ‘ideal' state – and I would be foolhardy to suggest we can – we can certainly do better than writing critical-path code during Alpha or Beta phases in a project!How long is a piece of virtual string?
Although a game is a finite piece of software, it is rather tricky to describe criteria for “completion.” It is almost universally true that the functionality and features of games that we see on store shelves are only some percentage of the development team's ambitions. More will have been designed than implemented, and not all that was implemented will have been used. Given then that games rarely reach the “all done” mark, how are we to decide if a game is releasable? What metrics are available to inform us how much is actually ‘done and dusted'?
Consider also a problem of scheduling sub-tasks: say a programmer (call her Jo) has said it'll take 10 days to write the ‘exploding trap' object, and that she's 4 days into this time. Is her task 40% complete? It's very hard to tell, especially since we cannot see the trap exploding till maybe day 9 or 10. But let's be optimistic, and suggest that Jo works hard and gets the job ‘done' in 8. Usually there is a profit of +2 days marked up, the task is marked as complete, and everything looks hunky dory for the project.
Later on, it turns out that the trap needs to be modified since (say) it needs to be able to trap larger objects. It's another 4 days of work for our Jo, and now we have a deficit of –2 days, and suddenly the project starts to look like it's slipping.
The point is this: most objects in a game rarely get written just once. We'll revisit them over the course of a project to fix bugs, add and remove features, optimise and maybe even rewrite them entirely. This isn't a pathological behaviour: almost all significant software systems grow and evolve over the course of time. How naïve then does the ‘4 days in, 40% complete' metric look? Pretty damn naïve, to put it politely. What we really need is a system that allows time and space for evolution without driving projects into schedule loss and the resulting state of semi-panic that characterises most development processes.
Milestones round my neck
Almost all software development (outside of research, which by its nature it open-ended) is driven by some kind of milestone system. Let me state unequivocally now that this is a good thing: the days of anarchic commercial software development should be buried and remain so. Nevertheless, just by virtue of it being a ‘good thing' does not mean that it doesn't come with its own particular set of pros and cons. In particular, if we accept (for all the ‘pro' reasons) that milestone-driven development is the way to go then we must also pay attention to the ‘con' side that will inevitably frustrate our attempts to make the process work with the efficiency we require for delivery on time, within budget.
One of the most difficult cons games developers have to deal with is the different way that milestones and the associated schedules are interpreted by production teams and management. As most of those who have worked with non-trivial software products, or in fact any large project that requires multiple bespoke interacting component parts spanning a variety of disciplines, have come to realise, schedules represent a team's best guess at how the product will evolve over time.
On the other hand, management – perhaps unused to the way that schedules are produced, perhaps because they require correlation of studio funding with progress – often read the document completely differently. They see the document almost as a contract between themselves and developers, promising certain things at certain times.
This disparity between seeing a schedule as a framework for project evolution to facilitate tracking, and as a binding agreement to deliver particular features at particular times, causes much angst for both developers and managers. The former often have to work ridiculous hours under pressure to get “promised” features out. The latter have responsibility for financial balances that depend on the features being in place.Internal and external milestones
We can see that there are some basic premises about milestones that need to be addressed:
Clearly, the sort of milestones that managers need be aware of are ‘cruder' or ‘at a lower granularity' than the milestones that developers need to pace the evolution of the product. We can therefore distinguish between ‘external' milestones, which are broad-brush descriptions of high-level features with granularity of weeks (maybe even months), and ‘internal' milestones that are medium and fine-level features scheduled in weeks and days.
Managers therefore never need to know the internal mechanisms that generate the software. To adopt a programming metaphor, the team can be viewed as a ‘black box' type of object with the producer as its ‘interface'. There are two types of question (‘public methods', to extend the analogy) that a manager can ask of a producer:
This is an unrealistically simple example of interaction between production and management. The latter will want to know issues of team dynamics, why things are running late (as they inevitably seem to), and a whole host of other project-related information. However, it draws a fuzzy – but distinguishable – line in the sand between the scheduling of features and accountability for their development.The breaking-wheel of progress
There is one other important sense in which management and develop perceive milestones differently. It is based on the concept of ‘visibility' and is without doubt the biggest millstone (half-pun intended) around developers' necks this side of Alpha Centauri.
Almost ubiquitously in the industry, management refuses to regard features that they cannot see (or perhaps hear) within a short time of picking up the game as importantly as those obviously visible (or audible) ones. For those of us that work on AI, physics, memory managers, scripting systems, maths, optimisation, bug-fixing and all those other vital areas of a game's innards that are not open to visual inspection, this is particularly galling. To spend weeks and months working on hidden functionality only to have the team's work dismissed as ‘inadequate' because there was no new eye-candy is an all too common occurrence.
The education of managers in the realities of development is a slow, ongoing and painful process. Meanwhile, we developers have to work with what we are given, therefore it remains important to – somehow! – build ongoing visible / audible progress into the development of the project.
There is an intimate relationship between the concept of visibility and of completeness. Many tasks may not become tangibly present until they are ‘complete'. Saying that something is ‘40% complete', even if that were a rigorously obtained metric, might still amount to ‘0% visible'. So we'll only be able to fully address the issue of progress when we deal later with determining ‘completeness' for a task.Always stay a step ahead
Despite our best – though sometimes a little less – efforts, we will slip. We shall deliver a feature late or perhaps not even at all, and if the management is in a particularly fussy mood then there may be much pounding of fists and red faces. Worse than showing no visible progress would be to show retrograde progress – fewer features apparent than a previous milestone. Nevertheless it is a common and required ability for projects to arbitrarily disable and re-enable particular functionality within the code base. With the advent of version control systems, we are now able to store a complete history of source code and data, so in theory it is always possible to “roll back” to a previous version of the game that had the feature enabled.
Just because it's possible, does that make it desirable? In this case, yes. Indeed, I would argue that working versions of the game should be built frequently – if not daily at least weekly – and archived in some sensible fashion. When the management asks production for the latest version of the game (one of their two allowed questions from the previous section), then the producer will return not the current (working) build but the one previous to that.
Why not the current working build? Because it is important to show progress, and development must ensure that to the best of their ability the game has visibly improved from one iteration to the next. If it becomes necessary – and it usually does – to spend time maintaining, upgrading, optimising or rewriting parts of the code base, then releasing the next-but-one working version gives another release with visible improvements before we hit the ‘calm' spot with no apparent progress.
From one point of view, this is a ‘sneaky manoeuvre'. It's no more sneaky than (say) insuring your house against flood1. Publishers and managers always want to see the ‘latest' version and a development team itching to impress may well be tempted to show them it. Resist this urge! Remember, development should be opaque to management inspection other than through the supplied ‘interface'. Anything else is just development suicide.Iterated delivery
So we've decided that rather than work specifically to release code at external milestones, we'll supply ‘work in progress' builds at these times. Internally we'll be working to our own schedule. How should we organise this schedule?
I'll start by assuming that there is a reasonably comprehensive design document for the game (believe me, you'd be surprised the number of times there isn't). This document should describe, in brief, what the game is about – characters (if any), storyline (if any), situations and rules. Step one to producing an internal schedule is to produce the Object Oriented design diagram for the game. We are not interested here in the diagram specifying interrelationships between the objects; the end goal is simply to produce a big list of all classes that map directly to concepts in the game. Auxiliary classes such as containers and mathematical objects need not apply – we are only looking for classes that map to game-level concepts.
Once we have produced this list, it needs to be given back to the design team, as step two is really their call. They need to classify all the objects in the list (I'll use the terms ‘objects' and ‘features' interchangeably in this section) into the following three groups:
An executable that consists of working versions of these objects (coupled to the non-game classes) is generally not of playable, let alone releasable quality.
A game consisting of core and required features will be playable and releasable. Nevertheless, it should be considered the minimal amount of content that will be releasable, and still requires work if the game is to be near the top of the genre.
The end result is a list of features that is effectively sorted in terms of importance to the product. It is tempting to say that the optimal order of tasks is then to start at the top – the most important ‘core' tasks – and work our way down. We carry on completing tasks until we run out of time.
Well it's close, but there's no cigar for that method. There are fundamental problems in organising work this way. There is little evidence of anything that resembles ‘continual progress'. In the pathological case, the game is in bits for the entire development cycle until just before the end when the bits are pulled together and – hopefully! – fit. This is guaranteed to have producers and management biting their knuckles with stress. Furthermore, the most likely outcome is that components do not work together or have unforeseen side-effects that may involve radical re-design very late on in the project.
Clearly it is correct to do the most important tasks first and the superficial tasks last (time allowing). But if we wish to show continual improvement of the product, we shall need to be a little smarter. So we shall progress to the third phase of the Iterated Delivery method (the actual ‘iterated' part). We'll start again with the list of features, which because an Object Oriented Design process has generated them, map directly to classes.
Consider just one of these classes. How does it start off its life? Usually something like this:
// File Player.hpp
// File Player.cpp
Over the course of the product development, much will be added, much will also be removed, but generally the object evolves. This evolution can occur in one of two ways: firstly it can start with zero functionality and end up fully implemented. This is possible, but not very common. More realistically, the object is either fully or partially re-written to have more complex or more robust or more efficient behaviour over the duration.
So far, so obvious. But consider the formalisation of the principle that objects evolve: instead of evolving the feature from zero functionality at the start to full functionality at the end, consider writing versions of the full object functionality. We define the following four versions of the feature:
We'll refer to the particular phase an object is in at any point in the project as the level of the class. A level 1 object has a null implementation; a level 4 object is optimal.
Some points to note: first of all, some objects will not naturally fit into this scheme. Some may be so simple that they go straight from null to optimal. Conversely, some may be so complex that they require more than four iterations. Neither of these scenarios presents a problem for us, since we aren't really counting iterations per se. We're effectively tracking implementation quality. In the case of an apparently simple object, we can only test it effectively in the context of any associated object at whatever level it's at. In other words, systems and subsystems have a level, which we can define slightly informally as:
L(subsystem) = min j L(objectj)
L(system) = min i L(subsystemi)
with L () denoting the level of an object, system or subsystem. Applying this idea to the application as a whole,
L(application) = min k L(systemk)
or in simple terms, the application's level is the smallest of its constituent object levels.
Now we need to put the ideas of level and of priority together to get some useful definitions, which form the basis of Iterated Delivery.
An application is defined as of release quality if and only if its required features are at the nominal level.
An application is referred to as complete if and only if its desired features are at the optimal level.
From these definitions, we see that there is a sliding scale that starts from a barely releasable product all the way up to implementing and polishing every feature the design specifies. The product just gets better and better, and – provided that the tasks have been undertaken in a sensible order – can be released at any time after it becomes of release quality.