﻿# 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.

Source: https://laszlonemes.com/university/software-technology/final-product · Author: László Nemes

Milestone 4 · Software Technology

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.

10 min read `Definition of Done` `Clean code refactoring` `Edge and failure cases` `Coverage target` `Release and hotfix flow` `Demo and 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.

After this session you can

- check a feature against the Definition of Done before calling it finished
- refactor a method so its name and structure say what it does
- write edge-case and failure-case tests, not only the happy path
- release a milestone through a release branch, a tag and a merge back – and ship a hotfix
- plan a demo and a gameplay video that show the game and the process

## 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

```md
- [ ] 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 automatically
```

> If anything on the list is missing, the issue is not done – even if "the code already works".

In your assignment

**Freeze features, then fix**

Agree on a feature freeze for the start of the last sprint: after it, only bugs, tests, refactoring and the release go into `develop`. A feature that isn't finished by then is moved off the milestone – a finished core beats a half-finished extra.

## 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?

```java
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

```java
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. |

Good to know

**Public API documented**

Public classes and methods get a short JavaDoc / XML doc comment. In .NET the template's `Directory.Build.props` already reports a missing one as an error.

## 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).

> **Fig. 1** · One method, three groups of cases. The happy path proves the feature exists; the edge and failure cases prove it holds.
>
> *Diagram:* Three groups of test cases for one method, move(player, direction). Happy path: a free tile ahead, the player moves, the state changes once and the listener is notified once. Edge cases: standing on the map border, the only free tile left, two players aiming at the same tile. Failure cases: a wall ahead or the map edge leaves the player in place, and a move after game over throws an exception.

A parameterised test covers a family of edge cases in one method:

Every corner, every direction off the map

```java
@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

```csharp
[Fact]
public void StepAfterGameOverIsRejected()
{
    var game = Games.Finished();
    Assert.Throws<InvalidOperationException>(() => game.Step(Direction.Up));
}
```

Common mistake

**Testing only what you remembered to build**

The acceptance criteria are the checklist for tests: every criterion has one, and every error case in the use cases' alternative flows has one. If a bug turns up in manual testing, first write the test that reproduces it, then fix it – it can never come back unnoticed.

## 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](https://laszlonemes.com/university/software-technology/continuous-integration)), 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.

Good to know

**The number is a floor, not the goal**

Coverage says which lines ran during the tests, not whether their result was checked. 90% with assertions on edge and failure cases is worth much more than 100% from tests that only call methods.

## 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.

> **Fig. 2** · The release branch lets the team keep working on develop while the release is finished. Every change that reaches main – release or hotfix – goes back into develop.
>
> *Diagram:* The release flow. A release branch starts from develop and only gets release preparation. It is merged into main, main is tagged v3.0, and main is merged back into develop. A bug found after the demo gets a hotfix branch from main, which is merged into main, tagged v3.1, and again merged back into develop.

| 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

```shell
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 develop
```

On 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

```shell
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 develop
```

Common mistake

**Forgetting the way back**

If `main` isn't merged back into `develop`, the version bump and every fix made on the release or hotfix branch are missing from the next release – and the same bug comes back at the next demo.

## The 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).

> **Fig. 3** · A ten-minute demo plan: most of the time on the running game, a slice on the process, and different voices for different parts.
>
> *Diagram:* A ten-minute milestone demo split between three members: goal and scope, live gameplay, the process on the board with merge requests and CI, tests and coverage, and questions. Different members present different parts, and the presenters rotate across milestones.

In your assignment

**The gameplay video**

- A few minutes of real play, recorded from the released build (the one the v3.0 tag produced), not from the IDE.
- Walk through the core use cases in a natural order: start, play, the interesting rules, save and load, game over.
- Show one failure being handled well – a blocked move, a corrupt save rejected with a message.
- No editing needed. Link it from the README or attach it to the Release.

Good to know

**Live demos fail**

Rehearse once from the released build on a machine other than the developer's. If something breaks live, the video is your fallback – which is one reason the course asks for it.

## 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.
