On
main,./mvnw testFAILS, on purpose, with exactly 4 test failures inDeliveryFeeServiceTest. That is the exercise, not a broken checkout. Your job in Task 1 is to make those 4 go green by fixing the code.
A different shape of live-coding round: you are not writing from a blank file, you are handed a small, working-but-messy service and asked to understand it, fix it and then say how you would make it production-ready. This reproduces a Glovo (Delivery Hero brand) round reported in 2025: "a small service in CoderPad, one file with 4 failing tests. Understand it, fix the tests, then say how to make it production-ready (SOLID, naming, logging, magic numbers, splitting functions)". The same process had a bar-raiser recursion question, converting every key of a nested map from snake_case to camelCase, which is Task 3 here. It tests reading code before changing it, finding root causes rather than patching tests, money handling, testable time, and how you talk about code quality. Time box: 35 minutes.
- Java 21, JUnit 5, AssertJ 3, Maven wrapper. No Spring, no database.
- JDK 21+
./mvnw test. Expected result onmain:Tests run: 17, Failures: 4, Errors: 0, Skipped: 1. The 4 failures are inDeliveryFeeServiceTest(Task 1). The skipped one is Task 3's first test,@Disabled("task 3: remove this line to begin").- The service is
src/main/java/com/amigoscode/fixit/DeliveryFeeService.java. Its Javadoc is the pricing spec the tests encode.
One task at a time. Open INTERVIEW_TASKS.md, cover everything below the
current task, start a timer. Read the code and the failing test output out loud before you touch
anything.
git checkout solutions/fix-the-service then ./mvnw test (all green).
Reset to the skeleton: git checkout main -- java/fix-the-service.
Spoiler: the 4 bugs and what the refactor changed
The 4 bugs (Task 1)
- Free-delivery threshold off by one.
if (b > 30)should be>= 30: a basket of exactly 30.00 paid a fee. Test:basketOfExactlyThirtyGetsFreeDelivery. - Truncation instead of rounding, on doubles.
Math.floor(f * 100) / 100cuts 2.9665 to 2.96 instead of rounding half up to 2.97 (anddoublearithmetic can putx * 100just under a whole number, so floor can drop a cent even with no discount). Test:percentageVoucherRoundsHalfUpToTheCent. - Voucher applied twice.
EURO1subtractsdiscinside the voucher block and then again in the "apply discount" block, so 3.49 became 1.49. Test:euroOffVoucherTakesOneEuroOff. - Peak window end is inclusive.
h >= 12 && h <= 14makes all of 14:00 to 14:59 peak; the dinner window next to it correctly usesh < 21. Test:lunchPeakIsOverAtTwo.
The refactor (Task 2)
- Money is a
Moneyrecord holdinglongcents: exact sums,percentOffroundsHALF_UPin one place,minusFloorAtZeroso a discount can never become a refund. - Every magic number is a named field of a
PricingRulesrecord (PricingRules.STANDARD), injected into the service, so a new city or a pricing experiment is data, not an edit (open/closed). - Time comes from an injected
java.time.Clock; peak hours are evaluated in the clock's zone. No moreLocalTime.now()and no test-only overload. - Peak hours are
TimeWindowrecords with one half-opencontains([start, end)) instead of hand-written hour comparisons. - Vouchers are a
sealed interface Voucher(FreeDelivery,PercentOff,AmountOff) looked up through aVoucherCataloginterface: a new kind of voucher is a new record, the catalog can become a database or promotions service without touching the fee logic (single responsibility, dependency inversion). - The 100-line method is split into
requireDeliverable,qualifiesForFreeDelivery,extraDistanceFee,smallOrderFee,peakSurchargeandapplyVoucher, each a few lines, each named after the rule it implements.d,b,v,f,pare gone; inputs are aDeliveryRequestrecord. - No swallowed exception: a missing voucher is an
Optional, not a caughtNullPointerException; an unknown code is logged at INFO and ignored on purpose. Invalid input (distance out of range or NaN, negative basket) fails fast with a message that says what was wrong. System.out.printlnis replaced bySystem.Logger(SLF4J in a Spring service) at DEBUG, with no string building when DEBUG is off.- Tests: the original 15 scenarios kept with the same inputs and the same expected amounts, adapted
mechanically to the new API, plus new tests on both sides of every fixed boundary, time zones,
injected rules, and a
MoneyTest.