Java Guild course · Lesson 0007

Lesson 0007 · the narrative

The Commit Log Is Part Of The Submission

An assessor with forty submissions to mark will read your history before they read your code. Write it for them.

The brief asks for “a brief description of the approach taken”. Most candidates write a paragraph. You can do better: hand over a commit log where the approach is demonstrated, commit by commit, with the reasoning attached to the change it justifies.

The reframe

A commit message is not a note to yourself. It is a message to a reviewer who was not there, explaining why the diff below it exists. The diff already says what changed; the message exists to say why.

The seven rules, and the two that matter here

Chris Beams’ seven rules are the standard reference. All seven are worth following, but two do the heavy lifting:

RuleWhy it earns marks here
Subject line in the imperative mood, under 50 chars, no full stopReplace IOException with domain exceptions” completes the sentence “if applied, this commit will…”. Consistency signals someone who has worked on a team.
Body explains what and why, not howThis is where the marks are. The how is the diff. The why is the only thing you can contribute that the code cannot.

The same change, three ways

fixed bug

Tells the reviewer nothing. They now have to read the diff and guess your intent.

Changed borrowBook to throw AlreadyBorrowedException instead of
IOException and removed the boolean return type

Better, but it is only the diff in English. A reviewer can already see that.

Replace IOException with the supplied domain exceptions

borrowBook and returnBook signalled every failure with IOException,
although neither performs any I/O, and collapsed two distinct cases
into one branch: borrowing an already-borrowed book reported
"Book not found" for a book the library was holding.

Wires up AlreadyBorrowedException, BookNotFoundException and
NotBorrowedException, which were present in the repository but
unreferenced.

Now the reviewer knows the defect, the severity, and that you noticed the unused classes were a hint. That last sentence is worth more than the diff.

Conventional Commits: adopt it or don’t, but decide

Conventional Commits prefixes each subject with a type: feat:, fix:, refactor:, test:, docs:.

Argument
ForThe type instantly tells a reviewer whether to expect behaviour change. refactor: is a promise the tests did not change — a promise they can check.
AgainstIt is a convention for repos with automated changelogs and semantic-version releases. This has neither. It can read as ceremony.
The recommendation

Use it, consistently, and say so in one line of your README. The value here is not tooling — it is that refactor: versus fix: makes your make-it-work-then-make-it-right discipline visible at a glance down the log. And “I followed Conventional Commits” is a two-word answer to “how do you work in a team?”

What you must not do is use it for half the commits. Inconsistency is worse than either choice.

The commit script

Here is the whole submission as a sequence. Each line is one commit, at green. Full messages are on the commit script reference card.

#SubjectWhy it is its own commit
1chore: add starter project as suppliedThe most important commit in the repo. Everything after it is a visible diff against what Solirius handed over.
2build: compile with release 11 and set a real groupIdBuild hygiene, separate from any code change.
3test: pin existing behaviour with a golden masterThe safety net, before anything it protects.
4test: add unit tests for Book and LibraryThe Graduate requirement, discharged and identifiable.
5fix: replace IOException with the supplied domain exceptionsThe headline change. Include the re-approved golden master here.
6refactor: return Optional from searchBookBehaviour-preserving; keep it out of commit 5.
7fix: stop the saved format losing and corrupting dataThree related defects in one subsystem — one commit is right, three would fragment the story.
8refactor: extract LibraryRepository from LibraryThe DIP move. Separate, so the reviewer can check no assertion changed.
9refactor: extract LibraryConsole from MainPure extraction. Claim “golden master unchanged”.
10fix: handle menu input that is not a numberBehaviour change, so not folded into commit 9.
11feat: search by author as well as titleBonus. Clearly marked as beyond the required tier.
12feat: sort the book list by title or authorBonus.
13docs: document the approach, decisions and deviationsThe README. Last, so it describes what actually exists.
Rules for this log

Do this now

Task · 10 minutes · do it before you write any more code
cd ~/Desktop/java-guild-candidate-test-intermediate-main
git init -b main
git config user.name "Tom Spencer"
git config user.email "tomspencerlondon@gmail.com"

echo "libraryState.txt" >> .gitignore

git add -A
git commit

with the message:

chore: add starter project as supplied

Baseline commit of the unmodified Solirius starter, so that every
subsequent change is visible as a diff against what was handed over.

If you have already started editing, stash your work (git stash won’t help before git init — copy the files aside instead), commit the pristine starter first, then re-apply. That baseline commit is worth the ten minutes of faff.

Drill

Why commit the unmodified starter before making any changes?

  1. Git refuses to compute a diff for files added in the initial repo commit
  2. It makes every later change legible as a diff from what you were handed
  3. It preserves the original authorship metadata for the code Solirius wrote
  4. It provides a commit to revert to should the later refactoring go wrong

Git diffs the root commit fine, against the empty tree. Not a constraint.

This is the whole reason. Without it, the assessor sees one commit containing both their code and yours and cannot tell them apart. With it, git diff HEAD~12 is precisely and only your work — which is what they are marking.

Authorship is recorded as you, since you are making the commit. It preserves the code, not the credit — and your README should say the starter was supplied.

A genuine benefit, but a minor one. You would have that from any commit; the value here is specifically about the boundary between their work and yours.

Which belongs in a commit message body?

  1. A list naming each file that this particular commit has modified or added
  2. A summary of the algorithm the new code uses, written out step by step
  3. The problem that existed beforehand and why this approach was chosen
  4. A note of how long the change took and which parts proved most difficult

git show --stat already prints that, accurately and for free. Restating it by hand just creates something that can go stale.

The diff is the algorithm, and it cannot drift out of date. Prose describing how the code works belongs in the code, as a comment or a javadoc.

This is the content only you can supply. The code shows what it does now; nothing in the repository records what was wrong before or which alternatives you rejected. That reasoning is exactly what “the approach taken” means.

Interesting to you, irrelevant to a reviewer, and it invites a judgement about your speed that you do not want to raise.

You realise commit 5 contained a typo you fixed in commit 6. What should you do before submitting?

  1. Rebase interactively to squash the fix into the commit that introduced it
  2. Leave both, since rewriting history is bad practice on any git repository
  3. Add a further commit explaining that commit 6 corrects a slip in commit 5
  4. Reset back to commit 4 and redo the remaining work as a single clean commit

Right. The taboo on rewriting history applies to published branches other people have pulled. Nobody has ever seen this repository. Tidying it before it is read is the same courtesy as proofreading — and git rebase -i with fixup is exactly the tool.

The rule is “don’t rewrite shared history”, not “never rewrite history”. Reciting the shorter version here leaves noise in the artifact being marked.

Now three commits are involved in one small mistake, and you have drawn attention to it. Worse than either alternative.

This throws away the story. The commit-by-commit narrative is a large part of the value; collapsing it to protect one typo trades the asset for the blemish.

Read this

Primary source

Chris Beams — “How to Write a Git Commit Message”. Fifteen minutes, and the highest ratio of value to length on the whole reading list. Read the worked examples at the end, not just the seven rules.

Carry on

Paste me your git log before you package and I will read it the way an assessor will — top to bottom, without opening a single diff, and tell you whether the story holds together.