AI-Assisted Debuggingon the JVM
What to expect
Experience & intuition
Debugging approaches
What not to expect
A model leaderboard
One universal recipe
Beyond the JVM
My path to debugging
Debugging
Execution traces
Failing schedules
AI-Assisted Debugging
Debugging production
I start with println()
PricingService pricing = new PricingService(args.length == 0);Order order = new Order();Money result = pricing.price(order);if (!order.currency.equals(result.currency)) { throw new AssertionError("expected " + order.currency + ", got " + result.currency);}AssertionError: expected EUR, got USD at OrderDemo.main(OrderDemo.java:7)
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); if (cached != null) { return useCached(cached); } else { return fetchPrice(order); }}AssertionError: expected EUR, got USDWhich branch?
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); if (cached != null) { println("useCached"); return useCached(cached); } else { println("fetchPrice"); return fetchPrice(order); }}useCached
AssertionError: expected EUR, got USDWhich call?
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); println("sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached); if (cached != null) { println("useCached"); return useCached(cached); } else { println("fetchPrice"); return fetchPrice(order); }}sku=JVM-1, requested=EUR, cached=90 USD useCached AssertionError: expected EUR, got USD
Which values?
Agents add logs too
5/37An agent runs the same loop
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); if (cached != null) { return useCached(cached); } else { return fetchPrice(order); }}AssertionError: expected EUR, got USDTest failed: expected EUR, got USD
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); if (cached != null) { println("useCached " + cached); return useCached(cached); } else { println("fetchPrice " + order); return fetchPrice(order); }}useCached 90 USD AssertionError: expected EUR, got USD
Test failed: expected EUR, got USD
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); if (cached != null) { println("useCached " + cached); return useCached(cached); } else { println("fetchPrice " + order); return fetchPrice(order); }}useCached 90 USD AssertionError: expected EUR, got USD
Test failed: expected EUR, got USD
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); println("sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached); if (cached != null) { println("useCached " + cached); return useCached(cached); } else { println("fetchPrice " + order); return fetchPrice(order); }}sku=JVM-1, requested=EUR, cached=90 USD useCached 90 USD AssertionError: expected EUR, got USD
Test failed: expected EUR, got USD
class PricingService {Money price(Order order) { Money cached = prices.get(order.sku); println("sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached); if (cached != null) { println("useCached " + cached); return useCached(cached); } else { println("fetchPrice " + order); return fetchPrice(order); }}sku=JVM-1, requested=EUR, cached=90 USD useCached 90 USD AssertionError: expected EUR, got USD
Test failed: expected EUR, got USD
Currency is missing from the cache key
A EUR order gets a USD price
Logs need a feedback loop
ReLog · ASE 2026 · revised 26 August 2026
DeepSeek-V3 · 311 faulty Java methods · correct repairs
One round of logging
Add logs → Run → Diagnose
26.0%Improve the observations
Add logs → Run → Read output
↳ Revise logs → Rerun → Diagnose
31.2%println() has limits
Change a log
Recompile + rerun
Inside a dependency
No source edits
println() has limits
Change a log
Recompile + rerun
Inside a dependency
No source edits