{"id":"code-dev-java-guard-clauses-clock","audience":"developers","category":"coding","track":"coding","technique":"context","title":"Add input validation to a Java method with exact messages","free":false,"task":"QA found that createBooking accepts blank room codes, 0 nights and past dates. Add guard clauses at the top that throw IllegalArgumentException with exactly these messages: 'roomCode must not be blank', 'nights must be between 1 and 30', and 'checkIn must not be in the past'. The past-date check must use the class's clock field so tests can fix the date. Return only the updated method in exactly 1 code block of at most 20 lines, keeping the rest of it unchanged, with under 25 words of text.","materialLabel":"The service class (attached to your prompt automatically)","material":"// BookingService.java  (Java 17, Spring Boot 3, project: harborview-stays)\npackage com.harborview.booking;\n\nimport java.time.Clock;\nimport java.time.LocalDate;\nimport java.util.UUID;\n\npublic class BookingService {\n\n    private final BookingRepository repo;\n    private final PriceTable prices;\n    private final Clock clock;\n\n    public BookingService(BookingRepository repo, PriceTable prices, Clock clock) {\n        this.repo = repo;\n        this.prices = prices;\n        this.clock = clock;\n    }\n\n    public Booking createBooking(String roomCode, int nights, LocalDate checkIn) {\n        int nightly = prices.nightlyRateCents(roomCode);\n        Booking booking = new Booking(\n            UUID.randomUUID().toString(),\n            roomCode.trim().toUpperCase(),\n            checkIn,\n            checkIn.plusDays(nights),\n            nightly * nights\n        );\n        return repo.save(booking);\n    }\n\n    public void cancel(String bookingId) {\n        repo.findById(bookingId).ifPresent(b -> repo.save(b.cancelled()));\n    }\n}\n\n// From the QA ticket HV-207:\n//   createBooking(\"   \", 3, today)          -> saved a booking with room \"\"\n//   createBooking(\"B12\", 0, today)          -> saved a 0-night booking, $0\n//   createBooking(\"B12\", 2, 2026-01-04)     -> saved a booking in the past\n// Test setup uses Clock.fixed(Instant.parse(\"2026-11-06T09:00:00Z\"), ZoneOffset.UTC)","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":20},{"type":"maxProseWords","max":25}],"mistakes":[{"title":"Letting the AI write the messages","why":"Tests assert on the exact message, and the AI writes its own wording.","fix":"Paste them: \"Messages exactly: roomCode must not be blank / nights must be between 1 and 30 / checkIn must not be in the past.\"","checks":["r0"]},{"title":"Ignoring the injected clock","why":"LocalDate.now() reads the real date, so tests with a fixed clock can't pass reliably.","fix":"Add: \"Use LocalDate.now(clock) with the existing clock field.\"","checks":["r1"]},{"title":"A rewritten class","why":"The AI reprints the class, adds a validator helper or switches to Bean Validation annotations.","fix":"Add: \"Return only createBooking, guards at the top, the rest unchanged.\"","checks":["r2","maxCodeLines"]},{"title":"An explanation of every check","why":"The AI explains each guard and suggests extra ones.","fix":"Add: \"One code block, no text.\"","checks":["codeBlocks","maxProseWords"]}],"promptHints":[{"label":"Gives the exact messages","patterns":["must not be blank","between 1 and 30","in the past"]},{"label":"Mentions the clock","patterns":["clock","now\\(clock\\)"]},{"label":"Names the exception","patterns":["IllegalArgumentException"]},{"label":"Asks for only the method","patterns":["only.{0,20}(method|createBooking)","just the method"]},{"label":"Protects the existing code","patterns":["unchanged","keep the rest","don'?t change","no new imports"]}],"hint":"Tests check exact messages and fix the date with a clock. Tell the AI both, and that you want just the method back.","rubricLabels":["Throws IllegalArgumentException with exactly the three messages 'roomCode must not be blank' (null or blank), 'nights must be between 1 and 30' (below 1 or above 30) and 'checkIn must not be in the past'","The past-date check compares checkIn with LocalDate.now(clock) using the existing clock field, not LocalDate.now() with no argument","Only the createBooking method is returned, the guard clauses come before the existing first line, and the existing body lines after them are unchanged (no new imports, helpers or annotations)"]}