Checking Assumptions While Debugging
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Checking assumptions is critical because most bugs happen where code behaves differently from what the developer expected.
An assumption is any belief you have about how the code works without explicitly verifying it.
For example, you might assume a repository method always returns a customer object, but if the customer doesn't exist in the database, it returns an empty Optional. If you don't check that assumption and just call dot-get, you get a NoSuchElementException at runtime. The root cause isn't the line that threw the exception—it's the upstream assumption that the customer would always exist.
When I debug, I start by listing my assumptions. Does this method return null? Is this list sorted?
Did this API call succeed? Can this variable ever be negative? Then I verify each one with logging or the debugger.
I'll add a log statement right before the crash to print the actual value and see if it matches what I expected. Often, the bug becomes obvious once I see the real data. I learned this the hard way in a project where I assumed a third-party API always returned JSON, but sometimes it returned an HTML error page when rate-limited.
My code crashed trying to parse HTML as JSON. Now I explicitly check response content-types and status codes instead of assuming success. Checking assumptions turns invisible mental models into explicit, testable conditions.
Checking Assumptions While Debugging
Direct Answer
Checking assumptions is critical because most bugs exist where code behaves differently from what the developer assumed. Explicitly verifying each assumption—like whether a method can return null, whether a list is sorted, or whether an API call succeeded—is the fastest path to finding the root cause.
Code Example
@Service
class OrderService {
private final CustomerRepository customerRepo;
private final InventoryService inventoryService;
public void processOrder(Long customerId, String productId) {
// Assumption 1: Customer exists in database
var customer = customerRepo.findById(customerId)
.orElseThrow(() -> new NotFoundException("Customer not found"));
// Assumption 2: Inventory service returns non-null
var available = inventoryService.checkStock(productId);
if (available == null) { // Explicitly check the assumption
throw new IllegalStateException("Inventory service returned null");
}
// Assumption 3: Available means quantity > 0
if (available <= 0) {
throw new OutOfStockException("Product unavailable");
}
// Each assumption is now verified with explicit checks
createOrder(customer, productId);
}
}
Common Mistakes & Pitfalls
- Trusting error messages too much: Freshers treat the line number in a stack trace as the root cause, but that's just where the symptom appeared—the actual bug is often several calls upstream where an assumption was violated.
- Assuming optional dependencies are present: Developers write code assuming a database is reachable, a cache is warmed, or a config file exists, then don't handle the case where those assumptions fail at runtime.
- Not reading documentation: Freshers assume methods behave a certain way without reading Javadoc—for example, assuming String.split always returns at least one element when it can actually return an empty array.
- Assuming thread-safety: Junior developers assume Spring singleton beans are thread-safe by default, leading to race conditions when instance variables hold request-specific state.
Check your understanding
Answer these without looking back at the article.
- How do assertions help with debugging in Java?
- What is defensive programming and how does it relate to debugging?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.