Java Guild course · Lesson 0001

Lesson 0001 · reading the brief

What This Test Is Actually Testing

The README describes a blank page. The repository contains a finished program. Only one of those is the exercise.

Open the README and it reads like a first-year assignment: “Create a simplified Library Management System…”, followed by a list of classes and method signatures. A candidate who reads only that will start typing class Book and never notice that Book already exists, fully written, in the repository they were sent.

This lesson gets you to the point where you can describe, out loud, what Solirius is really asking for — before you write a line of code.

The reframe

This is not a greenfield exercise. It is a legacy-code exercise: here is a working program with defects in it, extend it and improve it, and show us your judgement. The README's method list is a description of what you were given, not a specification of what to build.

Do this now, before reading on

Task · 10 minutes

Build it, then break it with your own hands. Three commands:

cd ~/Desktop/java-guild-candidate-test-intermediate-main
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
~/.local/maven/bin/mvn clean test

Then run the program and type a book title that does not exist:

printf '4\nGone With The Wind\n' | \
  ~/.local/maven/bin/mvn -q compile exec:java 2>/dev/null || \
  "$JAVA_HOME/bin/java" -cp target/classes com.solirius.intermediate.library.Main <<< $'4\nGone With The Wind\n'

Then run it again and type banana at the menu. Come back when you have seen both stack traces with your own eyes.

What the build tells you

Here is the interesting line from mvn test on the code as supplied:

[INFO] --- surefire:3.2.5:test (default-test) @ java-guild-candidate-test ---
[INFO] No tests to run.
[INFO] BUILD SUCCESS

The brief's Graduate tier — the tier every candidate must clear before their own tier counts — asks for “Unit Tests: Write test cases for Book and Library classes”. There are none. Zero. The build is green because there is nothing to be red about.

The first thing an assessor greps for

src/test/. It does not exist in the repository you were sent. Whatever else you do, that directory must be full and meaningful.

The loudest signal in the repository

Look at what is in src/main/java/…/library/exceptions/:

exceptions/
├── AlreadyBorrowedException.java
├── BookNotFoundException.java
├── NotBorrowedException.java
└── package-info.java

Three exception classes, each fully written with javadoc. Now grep the rest of the source for any of those names:

$ grep -rn "AlreadyBorrowedException\|BookNotFoundException\|NotBorrowedException" \
    src/main/java --include=*.java | grep -v "^src/main/java/.*exceptions/"
(no output)

Not one of them is referenced anywhere. They are imported by nothing, thrown by nothing, caught by nothing. They are dead code.

Now read the Intermediate requirement again, slowly:

Exception Handling: Handle scenarios where a book is already borrowed, does not exist, or cannot be returned.

Three scenarios. Three exception classes, named after exactly those three scenarios, sitting unused in the repository. This is not an accident and it is not a coincidence. It is the exercise, left on the table with a note on it.

What the code does instead

Here is Library.borrowBook as supplied:

public boolean borrowBook(final String title) throws IOException {
    Book book = searchBook(title);
    if (book == null || !book.borrowBook()) {
        throw new IOException("Book not found: " + title);
    }
    return true;
}

Three separate things are wrong with those five lines, and you should be able to name all three in one breath:

  1. The exception type is a lie about the category of failure. IOException means a stream, a file, a socket failed. Nothing here performs I/O. A caller who sees IOException in the signature will reasonably conclude this method touches a disk.
  2. The message is a lie about the facts. The || collapses two entirely different situations — no such book and the book is here but already on loan — into one branch with one message. Borrow the same book twice and the library tells you it has never heard of it.
  3. The return value is dead. The method returns boolean, but the only path that reaches return returns true. It can never return false. Every caller writing if (library.borrowBook(t)) is writing a branch that cannot be taken.
Verified, not guessed

Every claim in this course was reproduced against the real starter code on 2026-08-30. Borrowing an already-borrowed Dune really does produce:

java.io.IOException: Book not found: Dune

The book is right there. The library is lying to you. See the defect inventory for the full list with reproduction steps.

Read the pom as a hint sheet

Most candidates skim pom.xml for the Java version and move on. Read it as a statement of what a good answer contains:

What is declaredWhat is uses it todayWhat it is telling you
junit-jupiter 5.10.0NothingTests are expected, in JUnit 5, not 4.
mockito-core 5.15.2NothingThey expect classes with collaborators — i.e. that you will not leave everything static in Main.
sqlite-jdbc 3.47.2.0NothingThe Advanced tier's “portable file-based database”. Note it is compile scope, not test.
maven-checkstyle-pluginReporting onlyStyle is graded. The existing code is written to pass sun_checksfinal parameters, javadoc on every member.
source/target 11The compilerNo records, no sealed types, no switch expressions. Work inside it deliberately.
The Java 11 constraint is real

Verified on this machine: javac --release 11 rejects records outright —

error: records are not supported in -source 11
  (use -source 16 or higher to enable records)

JDK 25's compiler still accepts --release 8, 11, 17, 21 and 25, so staying on 11 is safe on any assessor's machine. Knowing precisely what you gave up — and saying so — scores better than quietly bumping the version.

Drill · what is this repository asking for?

Why does the presence of an unused exceptions package matter more than any other single detail?

  1. Unused classes always fail a Checkstyle report and must be deleted first
  2. Dead code in a repository is the defect the assessors want removed here
  3. It maps one-to-one onto the three scenarios your tier must handle
  4. Custom exceptions are required whenever a method can fail in Java code

Checkstyle's Sun rules do not flag unused classes, and deleting them would be the exact opposite of what is wanted.

Deleting them would throw away the strongest hint in the repository. They are not clutter — they are an unopened gift.

Right. Already borrowed, does not exist, cannot be returned — the Intermediate requirement lists three scenarios and the repository ships three exception classes with matching names. Wiring them up is the single highest-value change you can make.

Plenty of failures are properly signalled by a return value or an Optional. The argument for exceptions here is specific to this brief, not general.

What is the strongest argument against throws IOException on borrowBook?

  1. Checked exceptions are widely considered a mistake in modern Java design
  2. It describes the wrong abstraction, so callers must know the implementation
  3. It forces every caller to wrap the call in a try-catch block to compile it
  4. IOException is too general a type to carry a meaningful diagnostic message

A real debate, but not this one — you can hold either view and still see that this particular type is wrong. Argue the type, not the category.

This is Effective Java Item 73: throw exceptions appropriate to the abstraction. IOException leaks a claim about file handling into a method that only inspects a list in memory. It makes the caller reason about the implementation to understand the signature.

A domain exception would too, if you keep it checked. The try-catch is not the objection; what the type means is.

It carries the message fine. The problem is that the message is attached to a type that misdescribes what went wrong.

The brief says “visibility at candidate discretion” beside every class and method. Why?

  1. Design decisions are being marked, so they refuse to make them for you
  2. The assessors have no strong preference between public and private members
  3. Different Java versions enforce different default visibility rules for fields
  4. It allows candidates to submit the work as a module with exported packages

Read it alongside the evaluation criterion “proper use of object-oriented programming principles”. They are declining to specify encapsulation because encapsulation is the thing being assessed. A brief that told you to make books private would be marking nothing.

They have a very strong preference — the criteria say so. What they lack is a wish to hand you the answer.

Default visibility rules have not changed. Nothing about Java 11 versus 21 bears on this.

Nothing in the project uses the module system; there is no module-info.java and no requirement for one.

Say it out loud

If an interviewer opens with “so, tell me about the exercise”, the first thirty seconds decide how the rest goes. Rehearse this shape:

“The first thing I noticed was that the README describes building it from scratch, but the repo already has a working implementation — so I read it as a legacy-code exercise instead. There were three exception classes in the repo that nothing referenced, and the Intermediate requirement names exactly those three scenarios, so that told me where to start. And mvn test said no tests to run, which is the Graduate requirement unmet.”

Read this

Primary source

The brief itself — README.md in the starter repo. Read it three times. Once for the requirements, once for the Evaluation Criteria at the bottom, and once asking “which sentence in here does the code I was given already violate?” The five evaluation criteria are the marking scheme; everything this course does is aimed at them.

Carry on

Disagree with any of this? Bring it to me. If you think the IOException is defensible, argue it — being able to defend the code you were given is as much a skill as criticising it.