Vasu Patel logoVasu Patel
← Whiteboard to Blueprint: HLD & LLD
All chapters in this track
  1. 01Chapter 1: What HLD and LLD Are, and How They Differ
  2. 02Chapter 2: Gathering and Clarifying Requirements Before Designing Anything
  3. 03Chapter 3: HLD Deliverables: Architecture, Components, Technology Choices, Data Flow
  4. 04Chapter 4: LLD Deliverables: Class Diagrams and Sequence Diagrams
  5. 05Chapter 5: LLD Deliverables: Database Schema and API Contracts
  6. 06Chapter 6: Documenting Decisions: ADRs and Communicating Trade-offs to Stakeholders
  7. 07Chapter 7: Moving Cleanly from HLD to LLD Without Losing Intent
  8. 08Chapter 8: Common Mistakes: Over-Designing, Under-Specifying, and Skipping Non-Functional Requirements
  9. 09Chapter 9: When LLD Contradicts Its Own HLD
  10. 10Chapter 10: HLD and LLD in Design Reviews: What Reviewers Actually Look For
  11. 11Chapter 11: Worked Example: Ride-Sharing Dispatch — Requirements Through HLD to LLD
  12. 12Chapter 12: Worked Example: Hotel Booking — Requirements Through HLD to LLD
  13. 13Chapter 13: Worked Example: Food Delivery — Requirements Through HLD to LLD
  14. 14Chapter 14: How HLD and LLD Show Up in Interviews and Design Reviews
  15. 15Chapter 15: Common Interview Questions About HLD and LLD

Chapter 8 of 15

Chapter 8: Common Mistakes: Over-Designing, Under-Specifying, and Skipping Non-Functional Requirements

Why this matters

Every principle covered in Parts 1-2 has a corresponding failure mode when applied poorly, and three specific mistakes recur across HLD/LLD work with enough frequency to deserve dedicated, explicit treatment: designing far beyond what's needed, designing without enough detail to actually be useful, and omitting non-functional requirements entirely from consideration.

Over-designing: solving problems that don't exist yet

[SENIOR INSIGHT] Directly the companion OOP book's Chapter 14 and Design Patterns book's Chapter 13 premature-abstraction/pattern-fever theme, applied at the HLD/LLD scale: an HLD that builds elaborate extensibility for a hypothetical future requirement with no current evidence of need, or an LLD with an abstract base class and interface for a component that has (and is likely to ever have) exactly one implementation, pays real design and implementation complexity cost for a flexibility benefit that may never be realized.

Over-designed HLD: building a fully pluggable, multi-provider payment
gateway abstraction for a startup's MVP that will only ever use one
payment provider for the foreseeable future -- the abstraction's cost
(more components, more indirection) is paid immediately and certainly;
its benefit (easy provider switching) is speculative and may never
materialize.

Right-sized HLD: integrate directly with the one payment provider
actually needed now, structured so that a FUTURE provider addition
would require a contained, well-understood refactor -- not a
speculative abstraction built and maintained before it's needed.

Under-specifying: leaving decisions that should be made now to "figure out during implementation"

[BEGINNER] The opposite failure: an HLD or LLD document vague enough that implementers effectively have to make significant design decisions themselves, ad hoc, during implementation — exactly the "design happens implicitly while writing code" failure mode the companion SDLC book's Chapter 5 identified as causing expensive implementation churn.

Under-specified LLD: "The BookingService handles booking creation and
validation." (No specific validation rules stated, no error handling
approach specified, no database schema provided -- an implementer has
to invent all of this during coding, with no design-time review of
those decisions at all.)

Right-sized LLD: specific validation rules stated explicitly, specific
error types and handling approach specified (companion Coding Standards
book, Chapter 5), a concrete database schema (Chapter 5 of this book)
provided -- an implementer executes a reviewed plan rather than
inventing one unreviewed, alone, mid-implementation.

Skipping non-functional requirements: designing only for the happy-path functional case

[BEGINNER] Directly the companion SDLC book's Chapter 4 point about non-functional requirements being the more commonly neglected category, now specifically in the HLD/LLD context: a design that thoroughly specifies functional behavior (what happens when a booking is created) while never addressing scale, latency, or availability requirements produces a design that might be functionally correct and still fail catastrophically in production the moment real load arrives — because nobody designed for that load at all.

The calibration skill: matching design depth to actual risk

[IN PRACTICE] All three mistakes are really the same underlying failure — miscalibrating design effort relative to actual risk and uncertainty — expressed in three different directions: over-designing wastes effort on low-probability future needs; under-specifying under-invests effort on decisions that genuinely matter now; skipping non-functional requirements entirely omits a dimension of risk from consideration altogether. The corrective skill, covered throughout this book (Chapters 1, 2, and 3's HLD deliverables explicitly including capacity estimates) is the same calibration judgment the companion SDLC book's Chapter 5 introduced: match design rigor to actual, current, evidenced stakes — not to a guess about the future, not to whatever level of detail happens to be convenient to write, and not by omitting an entire category of requirement because it's less visible than functional behavior.

Common mistakes

  1. Building speculative flexibility into an HLD or LLD for a requirement with no current evidence of need, paying real complexity cost for a benefit that may never materialize.
  2. Leaving significant design decisions unspecified, effectively pushing design work (and its associated risk of an unreviewed, ad hoc decision) into implementation.
  3. Producing a functionally thorough design that never addresses scale, latency, or availability requirements, risking a design that fails in production the moment real load arrives, despite being "correct" on paper.
  4. Treating these three failure modes as unrelated problems rather than recognizing them as the same underlying miscalibration (of design effort relative to actual risk), expressed in three different directions.

Checklist: avoiding these three failure modes

  1. Before adding flexibility/abstraction to a design, confirm there's real, current evidence of a need for it — not just a plausible-sounding future scenario.
  2. Before considering an HLD or LLD document complete, check whether an implementer would need to make any significant, unreviewed design decisions themselves — if so, the document is under-specified.
  3. Explicitly confirm non-functional requirements (scale, latency, availability) have been addressed in every HLD, using Chapter 3's deliverable checklist, not just the functional behavior.
  4. Treat design-effort calibration as a single, unified skill — matching rigor to actual, evidenced stakes — rather than separately worrying about over-designing, under-specifying, and non-functional requirements as unrelated concerns.

Discussion / interview questions

  1. "What's the risk of over-designing an HLD or LLD for flexibility a system doesn't currently need?" Strong answer: Pays real, certain complexity cost (more components, more indirection, more to understand and maintain) for a speculative future benefit that may never materialize — directly the same premature-abstraction risk covered at the code level in the companion OOP and Design Patterns books, now at the architecture/component-design scale.

  2. "What's the risk of an under-specified LLD document?" Strong answer: Implementers effectively have to make significant design decisions themselves, ad hoc, during coding — without design-time review, these decisions carry more risk and are more likely to cause the kind of expensive implementation churn a deliberate design phase (companion SDLC book, Chapter 5) exists to prevent.

  3. "Why might a functionally thorough design still fail badly in production?" Strong answer: If non-functional requirements (scale, latency, availability) were never addressed during design, the system might be functionally correct and still fail catastrophically the moment real production load arrives, since nobody designed capacity, caching, or redundancy for that load at all.

  4. "Are over-designing, under-specifying, and skipping non-functional requirements three separate problems, or one underlying issue?" Strong answer: One underlying issue — miscalibrated design effort relative to actual, evidenced risk and stakes — expressed in three different directions (too much effort on speculative needs, too little on decisions that matter now, and an entire risk dimension omitted altogether).

Chapter summary / cheat sheet

  • Over-designing: speculative flexibility for a need with no current evidence — pays certain complexity cost for a speculative benefit.
  • Under-specifying: leaving significant decisions to be made ad hoc during implementation — pushes unreviewed design risk into coding.
  • Skipping non-functional requirements: a functionally thorough design that never addresses scale/latency/availability — can fail catastrophically in production despite being "correct" on paper.
  • All three are the same underlying failure (miscalibrated design effort relative to actual risk) expressed in different directions — the corrective skill is the same calibration judgment covered throughout this book.