Lab 02 (Spring Boot / Java Track) β Defect Report
Lab: lab-02-requirements-to-code-java.md
Track affected: π© Spring Boot / Java only (.NET, Kotlin and Swift versions are separate files)
Date: 2026-09-21
Status: Open β lab implemented successfully with workarounds, lab document not yet corrected
Related: LAB_01_SPRINGBOOT_DEFECTS.md Β· LAB_02_SPRINGBOOT_TODO.md
Summary
The lab was written against a different codebase layout than the one in src-springboot/.
Package names, folder paths, the Task entity shape and the API surface all differ. Four
defects are blocking: following the lab literally produces code that does not compile, and
two steps instruct participants to violate the repository's own architecture rules.
Backlog Items 1β3 were implemented end to end despite this. The reference implementation is
green (182 tests, BUILD SUCCESS), but only because of the workarounds below.
| # |
Severity |
Defect |
Participant impact |
| 1 |
π΄ Blocking |
Wrong package names and file paths throughout |
Nothing compiles; files land in the wrong modules |
| 2 |
π΄ Blocking |
JPA annotations placed on the domain entity |
Violates Clean Architecture; omits all mapper work |
| 3 |
π΄ Blocking |
Claims POST /api/tasks does not exist |
The "RED phase" never happens as described |
| 4 |
π΄ Blocking |
Controller-local exception handler |
Silently overrides the global RFC 7807 handler |
| 5 |
π‘ Moderate |
mvn spring-boot:run fails as documented |
App will not start; no database |
| 6 |
π‘ Moderate |
No warning that existing tests break |
~80 call sites across 5 test classes break at once |
| 7 |
π‘ Moderate |
Hardcoded sample dates already in the past |
The "valid request" examples return 400 |
| 8 |
π‘ Moderate |
Troubleshooting advises the wrong repository pattern |
Would pull Spring Data into the domain module |
| 9 |
π’ Minor |
-Dtest= commands fail for unrelated reasons |
Confusing errors, wrong lesson |
| 10 |
π’ Minor |
Expected test counts do not match this repo |
Participants think they missed something |
| 11 |
π’ Minor |
Prerequisite "no uncommitted changes" is false |
Blocks a careful participant at step 0 |
Defect 1 β Wrong package names and file paths throughout
Severity: π΄ Blocking
Where
Roughly a dozen code blocks across Parts 2, 3 and 4.
What
| Dimension |
Lab says |
Repo actually has |
| Root package |
com.taskmanager.* |
com.example.taskmanager.* |
| Domain path |
src-springboot/domain/entities/Task.java |
src-springboot/taskmanager-domain/src/main/java/com/example/taskmanager/domain/tasks/Task.java |
| Application path |
src-springboot/application/services/ |
src-springboot/taskmanager-application/src/main/java/com/example/taskmanager/application/services/ |
| API layer name |
presentation |
api |
| Test path |
src-springboot/test/java/... |
src-springboot/<module>/src/test/java/... |
| Value objects |
domain/valueobjects/ |
No such package; TaskId/TaskStatus live in domain.tasks |
Why it matters
src-springboot is a four-module Maven reactor (taskmanager-domain, -application,
-infrastructure, -api). The lab describes a flat single-module layout that does not exist.
Every path in the lab is wrong, and every package declaration in every snippet is wrong.
Proposed fix
Correct all packages to com.example.taskmanager.*, all paths to real module paths, and
rename "presentation" to "api" throughout. Put Priority in domain.tasks alongside
TaskId and TaskStatus rather than inventing a valueobjects package β the repository
instructions call for feature-oriented packaging.
Workaround applied
Used the real paths and packages. Priority created at
taskmanager-domain/src/main/java/com/example/taskmanager/domain/tasks/Priority.java.
Defect 2 β JPA annotations placed on the domain entity
Severity: π΄ Blocking
Where
- Part 2.3 "Implement Task Entity (GREEN Phase)" β sample entity
- Part 3 "Key Learning Points" claims "Domain Purity: No Spring/infrastructure concerns in entities"
What
The sample Task entity is annotated for persistence:
@Entity
@Table(name = "tasks")
public class Task {
@Id private UUID id;
@Column(nullable = false) private String title;
@Enumerated(EnumType.STRING) private Priority priority;
...
}
The repository's domain Task is a pure POJO with zero JPA imports. Persistence is
handled by a separate TaskEntity plus a TaskMapper anti-corruption layer in the
infrastructure module.
Why it matters
Two problems, both serious.
First, it contradicts .github/instructions/springboot.instructions.md, which states
plainly: "Domain β no deps (pure Java, no Spring)". The lab's own "Key Learning Points"
section then congratulates the participant on domain purity they just destroyed.
Second, and more practically, because the lab assumes an annotated domain entity it never
mentions the mapping layer at all. A participant adding priority and dueDate by
following the lab will not touch:
TaskEntity β needs priority and due_date columns
TaskMapper.toEntity() / toDomain() β needs to map both new fields
TaskMapperTest β round-trip assertions need extending
The new fields would silently fail to persist.
Proposed fix
Rewrite Part 2.3 to add the fields to the pure-POJO Task, then add a distinct sub-step
covering the infrastructure round trip. Remove the JPA annotations from the sample entirely.
Workaround applied
Added priority/dueDate to the pure domain Task, then separately extended TaskEntity,
TaskMapper and TaskMapperTest.
Two subtleties the lab does not raise, both discovered during implementation:
reconstitute() must not re-validate the due date. Creation rejects past due dates.
Applying that rule on load makes any task whose deadline has passed permanently
unloadable. The stored value is now assigned directly.
reconstitute() was silently discarding createdAt. The pre-existing code accepted
the parameter and then overwrote it with LocalDateTime.now(), with a comment admitting
the limitation. Fixed while widening the signature, since the round-trip test now asserts
on it.
Defect 3 β Claims POST /api/tasks does not exist
Severity: π΄ Blocking
Where
Part 3.2 Step 2, "Write Integration Tests FIRST (RED Phase)".
What
The lab states:
Run the integration tests - they should FAIL with 404 Not Found (endpoint doesn't exist yet)
Expected result: All integration tests fail. This is the RED phase! β
TaskController already implements POST /api/tasks, and seven further endpoints:
GET /{id}, GET /, PATCH /{id}, PUT /{id}/start, PUT /{id}/complete,
PUT /{id}/cancel, DELETE /{id}.
Why it matters
The participant gets 201 Created, not 404. The lab's central TDD demonstration β watch it
fail, then make it pass β does not occur. Worse, a participant who trusts the lab may conclude
their test setup is broken and start debugging working code.
Proposed fix
Reframe Part 3.2 as extending an existing endpoint. The honest RED signal is an assertion
failure on the missing priority field in the response body, not a 404. That is still a
perfectly good TDD demonstration, and it is true.
Workaround applied
Extended the existing endpoint. CreateTaskRequest and TaskResponse were widened rather
than duplicated, preserving their existing @NotBlank / @Size validation and the
TaskResponse.from(Task) factory.
Defect 4 β Controller-local exception handler overrides the global one
Severity: π΄ Blocking
Where
Part 3.2 Step 3 β sample TaskController.
What
The sample controller declares its own handler and error shape:
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException ex) {
return ResponseEntity.badRequest()
.body(new ErrorResponse("Validation Error", ex.getMessage()));
}
record ErrorResponse(String error, String message) {}
The repository already maps IllegalArgumentException to a 400 RFC 7807 ProblemDetail in
GlobalExceptionHandler (@RestControllerAdvice), alongside TaskNotFoundException β 404,
IllegalStateException β 400, MethodArgumentNotValidException β 400 with a field-keyed
errors map, and a catch-all β 500.
Why it matters
A controller-local @ExceptionHandler takes precedence over @RestControllerAdvice.
Adding it silently changes the API's error contract from standards-compliant ProblemDetail
to a bespoke {error, message} shape β for this one controller only, producing an API that
is internally inconsistent. Existing tests asserting on $.title and $.errors.* would break.
Proposed fix
Delete the handler and the ErrorResponse record from the lab sample. Point participants at
GlobalExceptionHandler and explain that throwing the right exception from the domain is
sufficient β a genuinely better lesson than hand-rolling error responses in a controller.
Workaround applied
No local handler added. Priority.fromString(...) throws IllegalArgumentException, which
the existing global handler converts to a 400 ProblemDetail. Verified against a running
server:
priority "SUPER_URGENT" -> 400 Invalid priority: SUPER_URGENT. Valid values: [LOW, MEDIUM, HIGH, CRITICAL]
dueDate "2020-01-01" -> 400 Task due date must be in the future
priority omitted -> 400 errors.priority = Task priority is required
Defect 5 β mvn spring-boot:run fails as documented
Severity: π‘ Moderate
Where
Part 4.1 "Run the API".
What
The lab gives:
cd src-springboot
mvn spring-boot:run
Two independent problems.
a) Wrong profile. The default application.yml targets PostgreSQL at
localhost:5432/taskmanager with ddl-auto: validate. Without a running Postgres the
context fails to start.
b) Wrong module, and stale artifacts. spring-boot:run at the reactor root has no single
application to run. Targeting -pl taskmanager-api resolves taskmanager-domain from
~/.m2, which may predate the participant's own changes:
java.lang.NoClassDefFoundError: com/example/taskmanager/domain/tasks/Priority
This surfaces as HTTP 500 on every request, with the app otherwise appearing healthy.
Proposed fix
Document the verified sequence:
cd src-springboot
mvn clean install -DskipTests
mvn spring-boot:run -pl taskmanager-api -Dspring-boot.run.profiles=dev
Windows PowerShell requires quoting: "-Dspring-boot.run.profiles=dev".
The dev profile uses in-memory H2 with ddl-auto: create-drop and exposes the H2 console at
/h2-console. Worth mentioning, since participants will want to inspect the new columns.
Workaround applied
Used the sequence above. Verified: the app starts and
POST /api/tasks {"title":"Smoke test","priority":"critical","dueDate":"2027-04-15T17:00:00"}
returns 201 with priority=CRITICAL.
Defect 6 β No warning that existing tests will break
Severity: π‘ Moderate
Where
Part 2.2/2.3, where priority becomes a required constructor argument.
What
The lab treats Task.create(title, description, priority, dueDate) as though it were a new
method on a greenfield entity. In this repository, making priority required is a breaking
change to Task.create(String) and Task.create(String, String), which are called from:
| File |
Call sites |
TaskTest |
36 |
TaskServiceTest |
27 |
JpaTaskRepositoryAdapterIntegrationTest |
20 |
TaskMapperTest |
8 |
TaskApiIntegrationTest |
8 |
Around 80 call sites fail to compile the moment the signature changes, across four modules.
Why it matters
Participants hit a wall of compilation errors with no guidance, and no indication whether they
have made a mistake or this is expected. It is also a missed teaching opportunity: repairing
a test suite after a deliberate breaking change is exactly the kind of work TDD is supposed to
make safe.
Proposed fix
Add an explicit step: "Expect roughly 80 compilation errors across four modules. This is the
cost of making a field required β here is how to work through them." Then show the pattern.
Alternatively, offer the non-breaking variant (optional priority defaulting to MEDIUM) as a
documented fork in the road, with the tradeoff stated.
Workaround applied
Took the breaking change deliberately, then repaired all call sites. Full reactor green.
Defect 7 β Hardcoded sample dates are already in the past
Severity: π‘ Moderate
Where
- Part 3.2 Step 4 β
tasks.http sample uses 2026-04-15T17:00:00
- Part 4.3 β curl "Valid Request" uses
2026-04-25T17:00:00
- Part 4.3 β expected response body repeats
2026-04-25T17:00:00
What
Both dates are in the past as of 2026-09-21. The lab's own validation rule rejects past due
dates, so the examples labelled "valid" now return 400 Bad Request.
Why it matters
Self-contradicting examples. A participant following Part 4.3 exactly gets a validation error
from the request the lab says should succeed, and no explanation.
Proposed fix
Stop hardcoding. Either instruct "use any date at least a day in the future", or use a
far-future constant that will not expire during the lab's useful life. Add a note that these
values need periodic review.
Workaround applied
The delivered src-springboot/tasks.http uses 2027 dates and carries a comment explaining the
constraint.
Defect 8 β Troubleshooting advises the wrong repository pattern
Severity: π‘ Moderate
Where
Troubleshooting β "Repository Pattern Not Working".
What
Solution: Ensure repository extends JpaRepository<Task, UUID> in domain layer
Why it matters
Exactly backwards for this repository, and for Clean Architecture generally. TaskRepository
is a domain port β a plain interface with business-intent methods. JpaTaskRepositoryAdapter
in the infrastructure module implements it, delegating to a package-private
SpringDataTaskRepository extends JpaRepository<TaskEntity, UUID>.
Following this advice would pull Spring Data into the domain module and invert the dependency
direction the lab teaches three sections earlier.
Proposed fix
Replace with guidance on adding a method to the port and implementing it in the adapter. This
matters for Backlog Items 4 and 5, which both need new query methods.
Workaround applied
None needed β no new repository methods were required for Items 1β3.
Defect 9 β -Dtest= commands fail for unrelated reasons
Severity: π’ Minor
Where
Parts 3.1 Step 3, 3.1 Step 4, 3.2 Step 2, 3.2 Step 3.
What
The lab gives mvn test -Dtest=TaskServiceImplTest. Three separate traps:
- Surefire aborts on the first module with no match:
No tests matching pattern "TaskServiceImplTest" were executed!
- PowerShell splits unquoted
-D properties at the dot:
Unknown lifecycle phase ".failIfNoSpecifiedTests=false"
-pl <module> alone resolves stale dependency jars from ~/.m2, causing
NoClassDefFoundError at runtime β the same root cause as Defect 5b.
Proposed fix
# macOS / Linux
mvn test -Dtest=TaskServiceTest -Dsurefire.failIfNoSpecifiedTests=false
# Windows PowerShell β quotes required
mvn test "-Dtest=TaskServiceTest" "-Dsurefire.failIfNoSpecifiedTests=false"
Add a note: prefer the full reactor (mvn test), or -am, when a module's dependencies have
changed. Identical to Lab 01 Defect 5, plus the stale-jar trap.
Defect 10 β Expected test counts do not match this repo
Severity: π’ Minor
Where
Part 3.3 "Run Full Test Suite".
What
Unit tests: All passing (10+ for TaskServiceImpl, 11+ for Task entity)
Integration tests: All passing (6+ for TaskControllerIntegrationTest)
Actual totals after completing Items 1β3: 65 domain, 69 application, 27 infrastructure,
21 API β 182.
Proposed fix
Express as deltas ("add at least N tests coveringβ¦") rather than absolute totals, which drift
every time the reference implementation changes.
Defect 11 β Prerequisite "no uncommitted changes" is false
Severity: π’ Minor
Where
Prerequisites: "Repository at clean state: git status shows no uncommitted changes".
What
Lab 01's NotificationService output is uncommitted when Lab 02 begins, and Lab 01 never
instructs participants to commit.
Proposed fix
Either add a commit step at the end of Lab 01, or soften this to "commit or stash your Lab 01
work before starting".
Verification status of the reference implementation
cd src-springboot && mvn clean test β BUILD SUCCESS
| Module |
Tests |
Before |
After |
| taskmanager-domain |
65 |
36 |
+29 |
| taskmanager-application |
69 |
66 |
+3 |
| taskmanager-infrastructure |
27 |
25 |
+2 |
| taskmanager-api |
21 |
8 |
+13 |
| Total |
182 |
135 |
+47 |
Backlog Items 1β3 complete; Items 4β5 not started. See
task-priorities-backlog.md.
Deliberate deviations
Not defects, but flagged so reviewers do not read them as mistakes:
- Concrete
TaskService, no interface. The lab introduces TaskService +
TaskServiceImpl. The repo has a single concrete class, consistent with the instruction to
avoid unnecessary abstractions. A one-implementation interface adds a file and no value here.
Priority in domain.tasks, not domain.valueobjects. Matches the existing placement
of TaskId and TaskStatus, and the feature-oriented packaging rule.
- Explicit
int value on Priority. Follows the lab's own "recommended" variant. Worth
noting it duplicates ordinal(); the justification is that an explicit value is stable
under reordering, whereas ordinal() is not.
Known gaps carried forward
- No schema migration. Production runs
ddl-auto: validate; dev and test run
create-drop, which hides the missing priority and due_date columns. A migration is
required before this reaches a real database. The lab never mentions migrations.
- No injected
Clock. Due-date validation reads LocalDateTime.now() directly, so
boundary conditions are untestable. The lab mentions Clock only in a troubleshooting
footnote. Backlog Item 5 ("due soon") will make this painful.
- Two different 400 body shapes. Bean-validation failures return a field-keyed
errors
map; domain failures return a plain detail string. Both are valid RFC 7807, but clients
must handle both. Worth a deliberate decision rather than an accident.
Suggested triage
Before the next workshop run β Defects 1β4. Participants cannot complete the Spring Boot
track by following the document as written.
Soon after β Defects 5β8. These produce confusing failures and teach at least one pattern
that actively contradicts the repository's architecture.
When convenient β Defects 9β11. Command corrections and copy edits.
Lab 02 (Spring Boot / Java Track) β Defect Report
Lab: lab-02-requirements-to-code-java.md
Track affected: π© Spring Boot / Java only (.NET, Kotlin and Swift versions are separate files)
Date: 2026-09-21
Status: Open β lab implemented successfully with workarounds, lab document not yet corrected
Related: LAB_01_SPRINGBOOT_DEFECTS.md Β· LAB_02_SPRINGBOOT_TODO.md
Summary
The lab was written against a different codebase layout than the one in
src-springboot/.Package names, folder paths, the
Taskentity shape and the API surface all differ. Fourdefects are blocking: following the lab literally produces code that does not compile, and
two steps instruct participants to violate the repository's own architecture rules.
Backlog Items 1β3 were implemented end to end despite this. The reference implementation is
green (182 tests,
BUILD SUCCESS), but only because of the workarounds below.POST /api/tasksdoes not existmvn spring-boot:runfails as documented-Dtest=commands fail for unrelated reasonsDefect 1 β Wrong package names and file paths throughout
Severity: π΄ Blocking
Where
Roughly a dozen code blocks across Parts 2, 3 and 4.
What
com.taskmanager.*com.example.taskmanager.*src-springboot/domain/entities/Task.javasrc-springboot/taskmanager-domain/src/main/java/com/example/taskmanager/domain/tasks/Task.javasrc-springboot/application/services/src-springboot/taskmanager-application/src/main/java/com/example/taskmanager/application/services/presentationapisrc-springboot/test/java/...src-springboot/<module>/src/test/java/...domain/valueobjects/TaskId/TaskStatuslive indomain.tasksWhy it matters
src-springbootis a four-module Maven reactor (taskmanager-domain,-application,-infrastructure,-api). The lab describes a flat single-module layout that does not exist.Every path in the lab is wrong, and every package declaration in every snippet is wrong.
Proposed fix
Correct all packages to
com.example.taskmanager.*, all paths to real module paths, andrename "presentation" to "api" throughout. Put
Priorityindomain.tasksalongsideTaskIdandTaskStatusrather than inventing avalueobjectspackage β the repositoryinstructions call for feature-oriented packaging.
Workaround applied
Used the real paths and packages.
Prioritycreated attaskmanager-domain/src/main/java/com/example/taskmanager/domain/tasks/Priority.java.Defect 2 β JPA annotations placed on the domain entity
Severity: π΄ Blocking
Where
What
The sample
Taskentity is annotated for persistence:The repository's domain
Taskis a pure POJO with zero JPA imports. Persistence ishandled by a separate
TaskEntityplus aTaskMapperanti-corruption layer in theinfrastructure module.
Why it matters
Two problems, both serious.
First, it contradicts
.github/instructions/springboot.instructions.md, which statesplainly: "Domain β no deps (pure Java, no Spring)". The lab's own "Key Learning Points"
section then congratulates the participant on domain purity they just destroyed.
Second, and more practically, because the lab assumes an annotated domain entity it never
mentions the mapping layer at all. A participant adding
priorityanddueDatebyfollowing the lab will not touch:
TaskEntityβ needspriorityanddue_datecolumnsTaskMapper.toEntity()/toDomain()β needs to map both new fieldsTaskMapperTestβ round-trip assertions need extendingThe new fields would silently fail to persist.
Proposed fix
Rewrite Part 2.3 to add the fields to the pure-POJO
Task, then add a distinct sub-stepcovering the infrastructure round trip. Remove the JPA annotations from the sample entirely.
Workaround applied
Added
priority/dueDateto the pure domainTask, then separately extendedTaskEntity,TaskMapperandTaskMapperTest.Two subtleties the lab does not raise, both discovered during implementation:
reconstitute()must not re-validate the due date. Creation rejects past due dates.Applying that rule on load makes any task whose deadline has passed permanently
unloadable. The stored value is now assigned directly.
reconstitute()was silently discardingcreatedAt. The pre-existing code acceptedthe parameter and then overwrote it with
LocalDateTime.now(), with a comment admittingthe limitation. Fixed while widening the signature, since the round-trip test now asserts
on it.
Defect 3 β Claims
POST /api/tasksdoes not existSeverity: π΄ Blocking
Where
Part 3.2 Step 2, "Write Integration Tests FIRST (RED Phase)".
What
The lab states:
TaskControlleralready implementsPOST /api/tasks, and seven further endpoints:GET /{id},GET /,PATCH /{id},PUT /{id}/start,PUT /{id}/complete,PUT /{id}/cancel,DELETE /{id}.Why it matters
The participant gets 201 Created, not 404. The lab's central TDD demonstration β watch it
fail, then make it pass β does not occur. Worse, a participant who trusts the lab may conclude
their test setup is broken and start debugging working code.
Proposed fix
Reframe Part 3.2 as extending an existing endpoint. The honest RED signal is an assertion
failure on the missing
priorityfield in the response body, not a 404. That is still aperfectly good TDD demonstration, and it is true.
Workaround applied
Extended the existing endpoint.
CreateTaskRequestandTaskResponsewere widened ratherthan duplicated, preserving their existing
@NotBlank/@Sizevalidation and theTaskResponse.from(Task)factory.Defect 4 β Controller-local exception handler overrides the global one
Severity: π΄ Blocking
Where
Part 3.2 Step 3 β sample
TaskController.What
The sample controller declares its own handler and error shape:
The repository already maps
IllegalArgumentExceptionto a 400 RFC 7807ProblemDetailinGlobalExceptionHandler(@RestControllerAdvice), alongsideTaskNotFoundExceptionβ 404,IllegalStateExceptionβ 400,MethodArgumentNotValidExceptionβ 400 with a field-keyederrorsmap, and a catch-all β 500.Why it matters
A controller-local
@ExceptionHandlertakes precedence over@RestControllerAdvice.Adding it silently changes the API's error contract from standards-compliant
ProblemDetailto a bespoke
{error, message}shape β for this one controller only, producing an API thatis internally inconsistent. Existing tests asserting on
$.titleand$.errors.*would break.Proposed fix
Delete the handler and the
ErrorResponserecord from the lab sample. Point participants atGlobalExceptionHandlerand explain that throwing the right exception from the domain issufficient β a genuinely better lesson than hand-rolling error responses in a controller.
Workaround applied
No local handler added.
Priority.fromString(...)throwsIllegalArgumentException, whichthe existing global handler converts to a 400
ProblemDetail. Verified against a runningserver:
Defect 5 β
mvn spring-boot:runfails as documentedSeverity: π‘ Moderate
Where
Part 4.1 "Run the API".
What
The lab gives:
cd src-springboot mvn spring-boot:runTwo independent problems.
a) Wrong profile. The default
application.ymltargets PostgreSQL atlocalhost:5432/taskmanagerwithddl-auto: validate. Without a running Postgres thecontext fails to start.
b) Wrong module, and stale artifacts.
spring-boot:runat the reactor root has no singleapplication to run. Targeting
-pl taskmanager-apiresolvestaskmanager-domainfrom~/.m2, which may predate the participant's own changes:This surfaces as HTTP 500 on every request, with the app otherwise appearing healthy.
Proposed fix
Document the verified sequence:
cd src-springboot mvn clean install -DskipTests mvn spring-boot:run -pl taskmanager-api -Dspring-boot.run.profiles=devWindows PowerShell requires quoting:
"-Dspring-boot.run.profiles=dev".The
devprofile uses in-memory H2 withddl-auto: create-dropand exposes the H2 console at/h2-console. Worth mentioning, since participants will want to inspect the new columns.Workaround applied
Used the sequence above. Verified: the app starts and
POST /api/tasks {"title":"Smoke test","priority":"critical","dueDate":"2027-04-15T17:00:00"}returns
201withpriority=CRITICAL.Defect 6 β No warning that existing tests will break
Severity: π‘ Moderate
Where
Part 2.2/2.3, where
prioritybecomes a required constructor argument.What
The lab treats
Task.create(title, description, priority, dueDate)as though it were a newmethod on a greenfield entity. In this repository, making priority required is a breaking
change to
Task.create(String)andTask.create(String, String), which are called from:TaskTestTaskServiceTestJpaTaskRepositoryAdapterIntegrationTestTaskMapperTestTaskApiIntegrationTestAround 80 call sites fail to compile the moment the signature changes, across four modules.
Why it matters
Participants hit a wall of compilation errors with no guidance, and no indication whether they
have made a mistake or this is expected. It is also a missed teaching opportunity: repairing
a test suite after a deliberate breaking change is exactly the kind of work TDD is supposed to
make safe.
Proposed fix
Add an explicit step: "Expect roughly 80 compilation errors across four modules. This is the
cost of making a field required β here is how to work through them." Then show the pattern.
Alternatively, offer the non-breaking variant (optional priority defaulting to
MEDIUM) as adocumented fork in the road, with the tradeoff stated.
Workaround applied
Took the breaking change deliberately, then repaired all call sites. Full reactor green.
Defect 7 β Hardcoded sample dates are already in the past
Severity: π‘ Moderate
Where
tasks.httpsample uses2026-04-15T17:00:002026-04-25T17:00:002026-04-25T17:00:00What
Both dates are in the past as of 2026-09-21. The lab's own validation rule rejects past due
dates, so the examples labelled "valid" now return 400 Bad Request.
Why it matters
Self-contradicting examples. A participant following Part 4.3 exactly gets a validation error
from the request the lab says should succeed, and no explanation.
Proposed fix
Stop hardcoding. Either instruct "use any date at least a day in the future", or use a
far-future constant that will not expire during the lab's useful life. Add a note that these
values need periodic review.
Workaround applied
The delivered
src-springboot/tasks.httpuses 2027 dates and carries a comment explaining theconstraint.
Defect 8 β Troubleshooting advises the wrong repository pattern
Severity: π‘ Moderate
Where
Troubleshooting β "Repository Pattern Not Working".
What
Why it matters
Exactly backwards for this repository, and for Clean Architecture generally.
TaskRepositoryis a domain port β a plain interface with business-intent methods.
JpaTaskRepositoryAdapterin the infrastructure module implements it, delegating to a package-private
SpringDataTaskRepository extends JpaRepository<TaskEntity, UUID>.Following this advice would pull Spring Data into the domain module and invert the dependency
direction the lab teaches three sections earlier.
Proposed fix
Replace with guidance on adding a method to the port and implementing it in the adapter. This
matters for Backlog Items 4 and 5, which both need new query methods.
Workaround applied
None needed β no new repository methods were required for Items 1β3.
Defect 9 β
-Dtest=commands fail for unrelated reasonsSeverity: π’ Minor
Where
Parts 3.1 Step 3, 3.1 Step 4, 3.2 Step 2, 3.2 Step 3.
What
The lab gives
mvn test -Dtest=TaskServiceImplTest. Three separate traps:No tests matching pattern "TaskServiceImplTest" were executed!-Dproperties at the dot:Unknown lifecycle phase ".failIfNoSpecifiedTests=false"-pl <module>alone resolves stale dependency jars from~/.m2, causingNoClassDefFoundErrorat runtime β the same root cause as Defect 5b.Proposed fix
Add a note: prefer the full reactor (
mvn test), or-am, when a module's dependencies havechanged. Identical to Lab 01 Defect 5, plus the stale-jar trap.
Defect 10 β Expected test counts do not match this repo
Severity: π’ Minor
Where
Part 3.3 "Run Full Test Suite".
What
Actual totals after completing Items 1β3: 65 domain, 69 application, 27 infrastructure,
21 API β 182.
Proposed fix
Express as deltas ("add at least N tests coveringβ¦") rather than absolute totals, which drift
every time the reference implementation changes.
Defect 11 β Prerequisite "no uncommitted changes" is false
Severity: π’ Minor
Where
Prerequisites: "Repository at clean state:
git statusshows no uncommitted changes".What
Lab 01's
NotificationServiceoutput is uncommitted when Lab 02 begins, and Lab 01 neverinstructs participants to commit.
Proposed fix
Either add a commit step at the end of Lab 01, or soften this to "commit or stash your Lab 01
work before starting".
Verification status of the reference implementation
cd src-springboot && mvn clean testβ BUILD SUCCESSBacklog Items 1β3 complete; Items 4β5 not started. See
task-priorities-backlog.md.
Deliberate deviations
Not defects, but flagged so reviewers do not read them as mistakes:
TaskService, no interface. The lab introducesTaskService+TaskServiceImpl. The repo has a single concrete class, consistent with the instruction toavoid unnecessary abstractions. A one-implementation interface adds a file and no value here.
Priorityindomain.tasks, notdomain.valueobjects. Matches the existing placementof
TaskIdandTaskStatus, and the feature-oriented packaging rule.int valueonPriority. Follows the lab's own "recommended" variant. Worthnoting it duplicates
ordinal(); the justification is that an explicit value is stableunder reordering, whereas
ordinal()is not.Known gaps carried forward
ddl-auto: validate; dev and test runcreate-drop, which hides the missingpriorityanddue_datecolumns. A migration isrequired before this reaches a real database. The lab never mentions migrations.
Clock. Due-date validation readsLocalDateTime.now()directly, soboundary conditions are untestable. The lab mentions
Clockonly in a troubleshootingfootnote. Backlog Item 5 ("due soon") will make this painful.
errorsmap; domain failures return a plain
detailstring. Both are valid RFC 7807, but clientsmust handle both. Worth a deliberate decision rather than an accident.
Suggested triage
Before the next workshop run β Defects 1β4. Participants cannot complete the Spring Boot
track by following the document as written.
Soon after β Defects 5β8. These produce confusing failures and teach at least one pattern
that actively contradicts the repository's architecture.
When convenient β Defects 9β11. Command corrections and copy edits.