Interview frequency: …
Short answer
Defensive Programming assumes all inputs are untrusted and checks them in every function to prevent crashes. Design by Contract defines an explicit agreement between caller and callee, with each side responsible for its obligations.
Explanation
Excessive defensive programming fills every internal function with repeated if (arg == null) checks, even inside a safe domain. DbC instead specifies that preconditions are the caller's responsibility (if you send a negative value, that is your error), while the implementer guarantees postconditions. Modern systems use strict defensive checks at external boundaries (API gateways, user input), while internal domain code relies on DbC and strong types to avoid repeated checks.
Code example
// System boundaries (Defensive Validation):
const body = validateSchema(req.body); // Strict defensive checks at the boundaries
// Internal domain logic (Contract):
executeTransfer(source, target, body.amount); // Trusts the validated typeCommon mistakes
- Applying defensive programming to every private internal function, bloating the code and hiding logic errors instead of exposing them in tests.