Ten engineering scenarios on a real Spring Boot project: understanding code, REST APIs, tests, debugging, refactoring, SQL tuning, review, documentation, Git and build troubleshooting, with real outputs.
Claude · 09 · Part 5
Working with Real Projects
Ten everyday engineering jobs, worked on the bookshelf project: understanding unfamiliar code, adding an API, testing, debugging a production-style failure, refactoring, tuning a query, reviewing, documenting, using Git and fixing a broken build. For each, you get the prompt pattern, what to verify, and — where possible — real output from a real run.
How to read this page honestly. No Claude Code session was run for this page. The code shown is what a good agent session should produce; it was written by this guide's author and then verified by real Maven, Git and curl runs, whose output is labelled Actual output. Where a step shows what an agent would be asked, it is a prompt pattern, not a recorded transcript. This is the same line the guide draws everywhere: executed means executed.
01
Scenario 1 · Understand
An Unfamiliar Codebase
You inherit bookshelf version 2. Start broad, then narrow — the docs' own recipe — and keep it read-only (Source 1). Delegating wide reads to a subagent keeps the file-by-file exploration out of your main context.
Prompts, in order (typed into a session)
give me an overview of this codebase
explain the main architecture patterns used here
what are the key data models?
how is a POST /api/books request validated and stored? name each class involved
use a subagent to find every place that touches the database
What a good answer should reconstruct: bookshelf v2
Verify: ask it to list the classes it used for each answer, then open two of them. For a typed language such as Java, a code-intelligence plugin makes this kind of tracing more precise by adding go-to-definition and find-references (Source 2).
02
Scenario 2 · Implement
Adding REST Endpoints
The task prompt (Chapter 8's five parts)
Move bookshelf from the in-memory list to JPA + H2. Add GET /api/books/{id} (404 as a ProblemDetail
for unknown ids) and POST /api/books (validate: title and author not blank, year 1450..2100;
invalid -> 400; return 201 with the created BookDto). Controllers return DTO records, never
entities. Follow the existing controller style, add tests for the 404 and the validation, and run
./mvnw -B test, showing me the output.
The result of that task is the code below (written by the author for this guide, then built and tested).
Actual output (status lines and bodies) · executed 2026-10-30
HTTP/1.1 404 Content-Type: application/problem+json
{"detail":"Book 99 not found","instance":"/api/books/99","status":404,"title":"Not Found"}
HTTP/1.1 400 (blank title rejected)
HTTP/1.1 201
{"id":7,"title":"Domain-Driven Design","author":"Eric Evans","year":2003}
7
Verify, beyond "it works": the 404 body is a standard ProblemDetail, the 400 came from validation (not a crash), the new book got id 7 after six seeded rows, and the list grew to seven. Read the diff for anything the task did not ask for.
03
Scenario 3 · Tests
Unit, Slice and Integration Tests
Level
bookshelf test
What it proves
Cost
Web slice
BookControllerTest (@WebMvcTest, service mocked)
Routing, validation, status codes, ProblemDetail shape
A passing test is evidence only if it can fail. The strongest proof in this project is that BookQueryCountTestdid fail on a real defect (next sections). When an agent writes tests for code it wrote, check that at least one fails when you break the behaviour on purpose.
04
Scenario 4 · Debug
A Production-Style Failure
A client reports "the API gives a 500 for a book that does not exist". The first prompt carries real evidence (Chapter 8): the request, the symptom and the stack. Here is that evidence, captured by running version 2 without the exception handler:
Actual output · GET /api/books/99 before ApiExceptionHandler existed
HTTP/1.1 500
{"timestamp":"2026-10-30T17:34:38.729Z","status":500,"error":"Internal Server Error","path":"/api/books/99"}
com.example.bookshelf.BookNotFoundException: Book 99 not found
at com.example.bookshelf.BookService.lambda$findById$0(BookService.java:25)
at com.example.bookshelf.BookService.findById(BookService.java:25)
at com.example.bookshelf.BookController.get(BookController.java:31)
The debugging loop, with evidence at each step
Prompt
GET /api/books/99 returns 500 instead of 404. Stack trace below. BookNotFoundException is thrown
in BookService.findById but never mapped. Add the mapping in ApiExceptionHandler (ProblemDetail, no
try/catch in the controller), add a regression test in BookControllerTest, and re-run the same
request with curl to confirm 404.
[paste the trace]
The fix is the ApiExceptionHandler shown in Scenario 2, and the "after" is the 404 application/problem+json response already shown. Notice the cause was not a bug in the lookup at all: the service correctly threw. The defect was a missing translation layer — a distinction an agent finds by reading the trace, and a reason to ask for the diagnosis before the fix.
A second real failure: the build itself
Earlier in the same work, version 2 would not even start under test. The Maven output pointed straight at the cause:
Actual output (excerpt) · first ./mvnw -B test of the JPA version
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement
"create table book ([*]year integer not null, author_id bigint, id bigint not null,
title varchar(255), primary key (id))"; expected "identifier"
[ERROR] Tests run: 5, Failures: 0, Errors: 2, Skipped: 0
[ERROR] BUILD FAILURE
The [*] marks the failing token: year, a reserved word in H2. The fix is a one-line column mapping, @Column(name = "publication_year"), now recorded in the project's CLAUDE.md (Chapter 10) so no future session repeats it. Two of five tests errored because both full-context tests failed to start the application — a reminder to read which tests failed and why before assuming the tests are the problem.
05
Scenario 5 · Refactor
Legacy Code Without Breaking Behaviour
The docs' refactoring recipe: find the legacy code, get recommendations, apply changes while maintaining the same behaviour, then run tests — in small, testable increments (Source 1). The risky part is "same behaviour". Here is a real example of why that sentence needs a check.
Legacy method: deeply nested ifs
staticString eraLegacy(int year) {
String result;
if (year < 1900) {
result = "classic";
} else {
if (year < 2000) {
result = "twentieth-century";
} else {
if (year < 2010) {
result = "2000s";
} else {
if (year < 2020) {
result = "2010s";
} else {
result = "recent";
}
}
}
}
return result;
}
staticString era(int year) {
if (year < 1900) return"classic";
if (year < 2000) return"twentieth-century";
if (year < 2010) return"2000s";
if (year < 2020) return"2010s";
return"recent";
}
The safety net: compare against the original over many inputs
Prerequisites: JDK 21. Single-file program comparing all three versions
$ java LegacyLabel.java
Actual output · executed 2026-10-30 · OpenJDK 21.0.2
years 0..3000 where behaviour differs from the legacy method:
attempt 1 (decision table): 1710
final (guard clauses): 0
first failing year for attempt 1: 190
What happened: the clever table looked right and read well, but it silently labelled every year from 190 to 1899 as "recent" (1710 years), because the table only covered quotients up to 18. A handful of spot checks would have missed it; comparing against the old implementation over a range found it at once. This is the pattern to ask an agent for: keep the old method as an oracle, compare over many inputs, then delete the oracle. And it is a live example of a confident-looking change that is wrong.
06
Scenario 6 · SQL
Finding and Fixing an N+1 Query
Listing books maps each Book to a DTO containing the author's name, but Book.author is lazy. Loading six books by three authors therefore runs one query for books and one per distinct author. Measured, not guessed — with Hibernate statistics in a test:
What to ask an agent: measure first, change second, show before-and-after counts, keep the test as a permanent guard. Be wary of "optimisations" without a measurement: "faster" is unfalsifiable, "1 statement instead of 4" is not. This page does not claim wall-clock gains — the data set is six rows in memory. Index advice for real databases belongs with your actual query plans, which an agent can read through a database MCP server (Chapter 10) if you connect one.
07
Scenario 7 · Review
Code Review
Claude Code includes bundled review commands: /code-review is listed among its bundled skills (Source 3) and /security-review runs an on-demand security pass over the changes on your current branch (Source 4). Both were left unrun for this page. Prompt patterns that work with or without them:
Prompts
review my changes on this branch against main. focus on correctness and security; skip style.
for each finding give the file and line, a failing scenario, and a minimal fix.
check the diff for new endpoints without validation or without tests.
A reviewer that must find something will. Anthropic's advice is to tell a review prompt what to ignore — style, in its example — because otherwise you end up with extra abstraction and tests for cases that cannot happen (Source 5). Treat findings as hypotheses: ask for the scenario that would trigger each one, and reject any you cannot reproduce.
08
Scenario 8 · Documentation
Docs That Stay True
The docs' recipe for documentation: identify undocumented code, generate documentation, improve it, and verify it follows project standards (Source 1). The risk is documentation that describes what the code "should" do. Counter it with the same discipline as tests: make the documentation executable.
Prompt
Write README.md for bookshelf: what it is, how to build and test, and an API table
(method, path, request body, responses). Every command and every curl example in it must be one
you have actually run in this session; show the output of each. Do not describe endpoints that
don't exist.
Verify: run the README's commands yourself on a clean checkout. The four curl examples and the ./mvnw -B test command in this chapter are the kind of content that belongs there, because each was run and its output is on this page.
09
Scenario 9 · Git
Git Operations
You can ask in plain language — "what files have I changed?", "commit my changes with a descriptive message", "create a new branch called feature/quickstart", "create a pr" (Source 1, Source 6). Whatever drives the Git commands, the resulting history is what you review. Here is the real history of this project's two steps:
* c12f41d Add JPA persistence, validation and 404 handling; fix N+1 on book listing
* 7672f6d Initial bookshelf: in-memory list endpoint
.../java/com/example/bookshelf/DataSeeder.java | 27 +++++++++++++++++
.../com/example/bookshelf/BookQueryCountTest.java | 31 +++++++++++++++++
...
15 files changed, 317 insertions(+), 12 deletions(-)
Branch per task. The JPA work lives on feature/jpa-books, so main still builds the v1 app. A bad agent run costs you a branch, not the codebase.
Read git diff --stat first. 15 files for "add persistence" is plausible; 60 would be a warning sign.
Pull requests. Claude Code can open them (gh pr create) and links the session to the PR for later resumption with claude --from-pr NUMBER (Source 1).
Remote actions need the most care. Pushing, force-pushing and deleting branches affect others; Chapter 10 shows a permission rule and a hook that block them.
10
Scenario 10 · Build and Deployment
Troubleshooting a Broken Build
Build failures are the best agent tasks: the error is precise and the check is "does it build". This one is real — the first ./mvnw -B test of the freshly generated project failed like this (Chapter 7):
Actual output (excerpt) · first build of the generated project
[FATAL] Non-resolvable parent POM for com.example:bookshelf:0.0.1-SNAPSHOT:
The following artifacts could not be resolved:
org.springframework.boot:spring-boot-starter-parent:pom:4.1.2.RELEASE (absent):
Could not find artifact org.springframework.boot:spring-boot-starter-parent:pom:4.1.2.RELEASE
in central (https://repo.maven.apache.org/maven2)
Prompt
./mvnw -B test fails with "Non-resolvable parent POM ... spring-boot-starter-parent:pom:4.1.2.RELEASE".
The project came from start.spring.io. Find out whether that version exists on Maven Central,
fix pom.xml minimally, and re-run the build. Do not change dependencies.
What the agent needs to do: check Central (the published list had 4.1.2, not 4.1.2.RELEASE), change one line in pom.xml, rerun. The "check Central" step matters: an agent without network access would only be guessing. State what it may use.
Deployment problems: the same shape
For an AWS deployment the pattern is identical — paste the real failing signal, name the environment, forbid destructive actions, ask for a diagnosis first. This prompt is a pattern; the aws commands were not run for this page.
Illustrative prompt — AWS commands not executed here
The bookshelf container task keeps stopping on ECS Fargate (service bookshelf-api, region eu-west-1).
Run only read-only commands: describe the service and the latest stopped task, then read the last
100 lines of its CloudWatch log group. Report the stop reason and the first error. Do not change
anything and do not run update, delete or deploy commands.
Why the boundary matters: an agent's permission rules can allow read-only aws calls and deny the rest; Chapter 10 shows how rules like Bash(aws *) interact (a broad deny cannot be overridden by a narrower allow).
11
Common Mistakes
Working on Real Code
1 · Trusting generated tests. Passing is not proof; confirm a test can fail (BookQueryCountTest did).
2 · "Same behaviour" by eyeball. The decision-table refactor read perfectly and was wrong for 1710 inputs. Compare against the original.
3 · Fixing symptoms. The 500 was a missing exception mapping, not a bad lookup. Ask for the diagnosis first.
4 · Optimising without measuring. Count statements or time a query before and after; keep the measurement as a test.
5 · Letting generated config through unread. The Initializr pom needed a fix; the H2 mapping needed one too. Generated is not verified.
6 · A giant diff nobody reads. Work on a branch, in small steps, and read git diff --stat before the details.
7 · Unbounded autonomy near production. Deployment prompts should say what is allowed, in words, and permissions should back the words up.
12
Summary
Key Takeaways, Exercises and Sources
∑ Working with real projects — what to remember
Explore read-only first, then act; ask for the files behind every answer.
Give each task a check: a test, a curl, a statement count.
Reproduce before you fix, and re-run the same reproduction after.
Refactors need an oracle. A plausible rewrite can be wrong for most inputs.
Measure performance work with a number that can fail a test.
Branch, commit small, read the diff. Git is the durable undo.
Exercises
Exercise 1 Beginner
Run ./mvnw -B test on the v2 project, then change @Column(name = "publication_year") back to the default and run it again. Read the error: which token does [*] mark?
Learning outcome: read a build failure to its root cause.
Exercise 2 Intermediate
Remove the @EntityGraph from BookRepository, add a fourth author with books, and predict the statement count before you run BookQueryCountTest. Then run it. Was your prediction right, and why?
Learning outcome: understand N+1 as a function of distinct lazy parents.
Exercise 3 Advanced
Take a legacy method from your own codebase. Write a comparison harness against the original over a wide input range, then ask Claude to refactor it. Record whether its first attempt passed the harness, and how many inputs differed if it did not.
Learning outcome: treat refactoring as behaviour-preserving only when measured.
Sources (read on 2026-10-30; paraphrased, at most one short quote per source)
Executed on this machine, 2026-10-30: Maven builds and tests (Spring Boot 4.1.2, Hibernate statistics, H2), the running app with curl, Git history and diff, and the Java refactoring comparison (OpenJDK 21.0.2 Temurin; Maven 3.10.0 via wrapper). Not executed: any Claude Code session, /code-review, /security-review, and the AWS commands. All code was written by the guide's author; none is a recording of an agent session.