A software project consists of both the code and the process by which you develop the code. It is important to formalize the process that you use. This means that you should have a set of documents describing your process, and that you should frequently look at and revise the documents while the project is underway.
There are many ways to separate out the different aspects of the software development process (as distinct from writing, testing, and debugging the code). Here we'll view the process as having four pieces.
We already discussed requirements and specifications. Now let's say a bit about the next three areas.
We put a number of things under the category of schedule: lifecycle, milestones, task list, QA plan, and risk management.
A software lifecycle is plan for what to do when, i.e. what order to carry things out in. Different projects use different kinds of lifecycle models. The lifecycle is a large enough topic that we'll devote a whole section to it a little later on in this chapter, eventually focusing on the Inventor lifecycle that you will use for your game project.
Setting milestones means (a) figuring out some definite, identifiable stages to reach, and (b) setting dates for when you plan to hit these milestones. As well as the finish-line milestone of shrink-wrap (or of posting your package on the Web), you have many preliminary milestones.
In a typical classroom project, your main milestones might be these.
Several times during the semester, you and the professor (the project stakeholders) need to make out a list of your remaining class meeting dates and figure out reasonable delivery dates for the milestones.
If you're using this book for self-study, pretty much the same kind of schedule might apply “ with the difference that you'll want to find friends or relatives to discuss the specifications with you and to view and test your demos.
One thing to realize about the milestones and the schedule is that they need to be continually revised “ like everything else in software engineering. Managers often make use of the Microsoft Project software to keep track of their schedule and their milestones.
As well as the main milestones, you may also want to think in terms of smaller milestones. In the case of a PacMan style game, getting to a presentable alpha build would involve, for instance, the milestone of creating a cMaze class or writing a method that simply builds such a maze out of cCritterWall objects.
When thinking about how to fit the smaller kinds of milestones into your schedule, it's useful to have a task list . This would be a list of all the things you need to do before the project is done. Particularly on your first few projects, you tend to underestimate the number of little extra tasks you're going to have to do near the project's end. These might include getting the bitmaps or art ready, making demonstration files, checking the help file against the program, and so on.
In making a schedule it's also important to allocate time for testing the program and testing how well the documentation matches the behavior. This is why software projects are usually divided into an alpha phase and a beta phase. The beta phase is when the testing and debugging takes place.
The process of testing is called QA , for quality assurance. It's important to allocate sufficient time to this, and to accept that you really need to retest after each new beta build. It's not unusual for a bug fix in one spot to break something somewhere else. In making out the schedule you need to consciously plan in enough time for sufficient QA. We'll say more about the testing process in Section 2.3: The Software Lifecycle.
As mentioned above, when you manage a software project you have to keep going back over your schedule and making sure that it matches the reality of what you've currently done. The process of risk management means looking ahead and trying to anticipate some of the possible ways in which you may go off schedule.
There are two main parts to risk management: monitoring and recovery. Monitoring means that you have to honestly admit what the most dangerous problems are so that you will immediately recognize them if and when they start to happen. If your program hinges on your being able to integrate a certain kind of image file into your code, there is a risk that you're not going to be able to do it. Monitoring means facing the fact that the worst can happen “ and persistently asking if it's happened yet. The risk of not being able to use a type of image remains until it's been demonstrated that it can be done. Because this task is a risk, it is not left until the very last minute.
The recovery aspect of risk assessment means formulating a Plan B, an alternate strategy to pursue if a given fear comes true. If, say, such and such a team member is unable to integrate *.gif image files into your code by the second alpha build, then you will reduce the risk by using, say, only *.bmp image files. If the team member who was supposed to provide your enemy creature's behavior algorithm stops coming to class or answering email, then someone else better start working on it, and if no one can, then you better figure out how to have a game in which the enemies simply use a default framework behavior algorithm.
Risk assessment monitoring is about having your team be honest with yourselves and not hiding your heads in the sand. Risk assessment recovery is about formulating Plan B, and, to mix up the metaphors, being willing to throw the stove and food out of your balloon basket if that's what it takes to stay aloft.
Design breaks into two levels: the high-level design and the detailed design . The high-level design is also known as the architecture , which tends to sound a bit more impressive.
The architecture or high-level design involves specifying the program's 'nouns' and its 'verbs', that is, the program's classes and the program's runtime behavior. The detailed design involves getting more specific about the classes and beginning to write out prototype code for them.
When we are doing the high-level design, we use a process known as object-oriented analysis to help figure out what classes we should use. This is a matter of singling out the key concepts used by your problem, and thinking about how best to represent the concepts as classes. Once you've decided which classes to use, the process known as object-oriented design helps you find the best way to make your classes work together.
Recall that we use 'UML' to stand for 'Unified Modeling Language'. A good way to talk about your class design is to use a UML class diagram, which is a bunch of rectangles representing classes, with lines showing the relationships among the classes. Drawing a class diagram is a good way to get a useful discussion going about the spec, and can be helpful in moving from the architecture to the detailed design. A class diagram on a whiteboard makes a focus for a group discussion of class design, and provides a non-technical channel by which coders and managers can usefully interact. Look ahead at the class diagrams of the Pop Framework in Chapter 3: The Pop Framework (Figures 3.4 and 3.10).
Describing the program's runtime behavior means figuring out the order in which things happen. How, for instance, does an animation program update itself? When a user clicks the mouse, what is the sequence of events we expect to have? UML sequence diagrams are very useful for sketching this out. Look at, for instance, some of the sequence diagrams in Chapter 6: Animation, for instance Figure 6.3 showing the sequence diagram of how our Pop programs animate the creatures onscreen.
The detailed design for your program states what members and methods your classes will have. In C++, the detailed design can consist of explicit definitions of the classes you will use; a good way to be precise about your classes is to go ahead and start writing up the formal class definitions as *.h header files. You can postpone the *.cpp implementation of the class methods for a little while. But you will find, once you do get into implementing a class's method, that you often need to rethink the original class design. This kind of back and forth is one of the enjoyable parts of object-oriented design. We say more about this in Chapter 4: Object-Oriented Software Engineering.
The most visible project document is the User's Guide, whether in printed or in help file form. For now suffice it to say that the User's Guide might typically include sections called Overview, Getting Started, Things to Try, and Controls, where the last section exhaustively describes the effect of each control found in the user interface.
Table 2.1. Documents for software development.
Looking back over the first three parts of our project process, we can imagine making a document (or set of documents) for each of them, that is a specification document, a schedule document, and a design document “ all these in addition to the User's Guide document.
Table 2.1 lists software process stages and some of the documents that might accompany them.
All documents should be visible to all the stakeholders involved in the project: the managers, the coders, and the customers. On a really well-run project, one might put all four pieces up on a website, possibly an intranet site or password-mandatory site rather than a public one.
None of these documents is set in stone. We expect that each of them is going to change somewhat during the project lifecycle, although there will normally be a 'feature freeze' date after which no further changes to the specification documents are allowed. But, up until that point, we are going to learn more about our project from the code, from the early builds, and from the way we see the schedule unfolding, so it's reasonable to keep changing things.
There are various models for how to update the documents. Either one person is in charge of maintaining each document, or they are changed during group meetings, or stakeholders might be allowed to 'check out' a copy of the document for revision, with the earlier versions being preserved.