Validate before changing state
Defensive programming protects contracts at boundaries without scattering redundant checks everywhere. Verify all preconditions before mutation so a failed operation leaves the object consistent.
class Budget(private var remaining: Int) {
init { require(remaining >= 0) }
fun spend(minutes: Int): Int {
require(minutes > 0)
require(minutes <= remaining)
remaining -= minutes
return remaining
}
}
Comparing against remaining capacity avoids computing an unchecked sum before validation. This example is not thread-safe: concurrent access still needs synchronization or confinement.
Use types to encode persistent rules where practical. Once a positive-duration value object has been validated, downstream code can depend on that contract instead of reparsing raw strings.
Exercise
Spend ten from a budget of 25, attempt to spend twenty, then spend fifteen. Confirm the rejected operation did not reduce the balance.
Check: validation should happen before file writes, network submissions, or mutable updates whenever feasible; rollback is harder than preventing an invalid operation.