Milestone 4 · Software Technology
The final product: clean code, tests, release
Milestone 4: finish against the Definition of Done – clean code, edge- and failure-case tests, high model coverage – then release through a release branch and a tag, and present a demo and a gameplay video.
The last milestone, in week 13, is the finished game: every feature works without errors, the code is clean, the unit tests are thorough – including edge and failure cases – and there is a short gameplay video. Three weeks is not much time, so this milestone is mostly about finishing and fixing, not about new features.
What "finished" means
A feature is finished when it meets the team's Definition of Done – not when the code runs on the author's machine. At M4 every feature of the game is held to it:
Definition of Done
- [ ] the feature works and the issue's acceptance criteria are met
- [ ] it has unit tests (model layer) and they pass
- [ ] the pipeline is green
- [ ] the MR was reviewed, its threads resolved, and it is merged into develop
- [ ] the wiki is updated if the design changed
- [ ] the issue closed automaticallyIf anything on the list is missing, the issue is not done – even if "the code already works".
Clean code
Clean code reads like a description of what it does. Short methods with one job, names from the game's language (Board, isWalkable, collectItemAt), no magic numbers, no duplicated blocks, comments only where the why isn't obvious.
Before – what does d == 1 mean? what is 2?
public void step(int d) {
if (d == 0 && p.y > 0 && m[p.y - 1][p.x] != 1) p.y--;
else if (d == 1 && p.x < m[0].length - 1 && m[p.y][p.x + 1] != 1) p.x++;
// ... two more branches like these
if (m[p.y][p.x] == 2) { s += 10; m[p.y][p.x] = 0; }
}After – the same behaviour, readable
public void step(Direction direction) {
Position target = player.position().next(direction);
if (!board.isWalkable(target)) return;
player.moveTo(target);
collectItemAt(target);
}| Smell | Fix |
|---|---|
| A method longer than a screen | Extract the steps into named methods; the original becomes a list of calls. |
Magic numbers: == 2, += 10 | Enums and named constants: TileType.ITEM, ITEM_SCORE. |
| The same block in three places | One method, called three times. |
| Commented-out code, debug prints | Delete them – git remembers the old version. |
| A model class importing the UI | Move the UI part to the view; the model notifies through a listener. |
| Compiler or analyser warnings | Fix them, don't suppress them. The pipeline should already block them. |
Tests that find bugs
Teams that only test the happy path ship games with spectacular bugs in the final demo: the player walks off the map, a second click on "Save" crashes, the game continues after game over. For every model method that can go wrong, think in three groups (Fig. 1).
A parameterised test covers a family of edge cases in one method:
Every corner, every direction off the map
@ParameterizedTest
@CsvSource({ "0, 0, LEFT", "0, 0, UP", "9, 5, RIGHT", "9, 5, DOWN" })
void moveOffTheMapLeavesPlayerInPlace(int x, int y, Direction direction) {
Game game = Games.emptyBoard(10, 6).withPlayerAt(x, y);
game.step(direction);
assertEquals(new Position(x, y), game.player().position());
}A failure case: the call is rejected
[Fact]
public void StepAfterGameOverIsRejected()
{
var game = Games.Finished();
Assert.Throws<InvalidOperationException>(() => game.Step(Direction.Up));
}Coverage target
For the final product, aim for about 90% line coverage of the model, shown by the coverage badge in the README. The view and the entry point are excluded from the measurement (see milestone 3), so the number speaks about the code that holds the rules. The template's coverage gate starts at 80%; raise it once you're above it, so coverage can't slide back in the last week.
The release flow
Each milestone is released the same way: a release branch from develop gets only release preparation, is merged into main, main gets a tag, and main is merged back into develop (Fig. 2). The tag starts the pipeline's package and release jobs, which put a downloadable build on the project's Releases page.
| Milestone | Tag |
|---|---|
| M1 Requirements and design | – (documentation only) |
| M2 Prototype I | v1.0 |
| M3 Prototype II | v2.0 |
| M4 Final product | v3.0 |
| A hotfix after a demo | v1.1, v3.1 … |
The tag is vMAJOR.MINOR: MAJOR is the milestone, MINOR the hotfix. A different member does the release at each milestone, in the days before the demo – not during the session.
Releasing v3.0
git switch develop && git pull
git switch -c release/v3.0
# version in the build file, README, small fixes only
git commit -am "bump version to 3.0" # the hook makes it "release: bump version to 3.0"
git push -u origin release/v3.0
# MR release/v3.0 → main, titled "Release v3.0": approve, green pipeline, merge
git switch main && git pull
git tag -a v3.0 -m "Release v3.0"
git push origin v3.0 # the pipeline packages the game and creates the Release
# MR main → develop, so the version bump and the fixes reach developOn the release branch only release preparation is allowed: the version in the build file, the README and wiki, small fixes found while trying the build. A bigger bug gets its own issue and a bugfix/ branch from the release branch. Never merge develop into the release branch – it would bring in unfinished work.
A hotfix after the demo
git switch main && git pull
git switch -c hotfix/ABC-99-crash-on-empty-save
# fix, commit, push, MR → main, approve, merge
git switch main && git pull
git tag -a v3.1 -m "Hotfix v3.1" && git push origin v3.1
# MR main → develop – put "Closes #99" in this one: issues close only on merges into developThe demo and the gameplay video
Each team gets 5–10 minutes. Show the game working, then show how it was built – and split the parts between members; the same person can't present every milestone (Fig. 3).
Summary
- Finished means the Definition of Done is met – criteria, tests, green pipeline, reviewed and merged, wiki updated.
- Clean code: short methods, names from the game, no magic numbers, no dead code, no warnings.
- Test the happy path, the edge cases and the failure cases; aim for about 90% coverage of the model.
- Release: release branch → MR into main → tag → MR main → develop. Hotfix from main, tag vX.1, merge back.
- Demo in 5–10 minutes with rotating presenters; record the gameplay video from the released build.