Loading content...
Here's a counterintuitive truth about design interviews: the candidates who openly discuss their design's limitations often score higher than those who present everything as flawless.
Why? Because every experienced engineer knows that no design is perfect. Every solution has constraints, every decision has downsides, and every architecture has edge cases that push its limits. When a candidate presents a design as if it has no weaknesses, one of two things is true:
Neither reflects well on the candidate.
In contrast, proactively acknowledging limitations demonstrates engineering maturity. It shows you understand that design is about trade-offs, that you've anticipated where your design might struggle, and that you're transparent about technical reality. These are hallmarks of senior engineering practice.
By the end of this page, you will master the art of identifying design limitations, learn how to communicate weaknesses without undermining confidence, develop frameworks for categorizing and prioritizing limitations, and understand how to propose mitigation strategies that demonstrate senior-level thinking.
Design limitations come in various forms. Understanding the taxonomy helps you systematically identify what might limit your design:
| Category | Description | Example Limitations | How They Manifest |
|---|---|---|---|
| Scalability | How the design handles growth | Single-threaded bottleneck, O(n²) algorithm, in-memory storage limits | Performance degrades as load increases |
| Complexity | Cognitive and maintenance overhead | Too many abstractions, intricate inheritance chains, pattern overload | Hard to understand, modify, or debug |
| Omissons | What was deliberately excluded | Not supporting a mentioned edge case, no offline mode, no audit trail | Missing functionality in specific scenarios |
| Performance | Speed and resource consumption | Memory-intensive caching, CPU-bound calculations, I/O blocking | Slow response times or high resource usage |
| Exceptions | Error and edge case handling | No graceful degradation, unhandled concurrent modifications, stale data tolerance | Failures in unusual circumstances |
Consider a parking lot system you've designed:
Scalability Limitations:
Complexity Limitations:
Omissions:
Performance Limitations:
Exception Handling Limitations:
Run SCOPE on any design and you'll find limitations. This isn't a sign of bad design—it's mathematical reality. The question isn't 'does this design have limitations?' but 'which limitations are acceptable for the stated requirements, and which would need future attention?'
Not all limitations are equal. Distinguishing between strategic and tactical limitations helps you prioritize which to discuss and how:
Fundamental constraints that would require significant redesign to address:
Example: "This design uses in-memory state, which limits horizontal scaling. Moving to distributed state management would require fundamental changes to our concurrency model."
Localized constraints that could be addressed with moderate effort:
Example: "The current find-available-spot algorithm is linear. If performance becomes an issue with large lots, we could add an availability index—the interface supports this evolution."
Treating strategic limitations as minor ("we could just add distributed caching") appears naive. Treating tactical issues as fundamental ("the whole design is compromised by this validation gap") appears alarmist. Correctly sizing limitations is key to credibility.
How you communicate limitations matters as much as identifying them. The LAMP Framework structures limitation communication for maximum impact:
| Element | Purpose | Example |
|---|---|---|
| Limitation | State the limitation clearly | "This design doesn't handle concurrent modifications to the same resource." |
| Acceptance | Explain why this is acceptable for now | "Given single-user interaction in current requirements, this is low priority." |
| Mitigation | Describe how it could be addressed | "If needed, we could add optimistic locking with version numbers." |
| Priority | Assess relative importance | "I'd rank this as a future concern, not a blocker for the current design." |
Example 1: Scalability Limitation
L: "This design uses a single PricingService instance. Under heavy load, this becomes a bottleneck."
A: "For the stated volume of 100 transactions per minute, a single instance is sufficient with headroom."
M: "If we scale to 10x volume, we'd refactor to stateless service with injected strategy, enabling horizontal scaling."
P: "Medium priority for roadmap; not blocking for MVP."
Example 2: Complexity Limitation
L: "The Observer pattern adds indirection that complicates debugging notification flows."
A: "The decoupling benefits outweigh debugging complexity for a system with multiple display clients."
M: "We could add logging at notification boundaries to improve traceability."
P: "Operational concern; would address during testing phase."
Example 3: Missing Functionality
L: "The current design doesn't support partial payments or split tenders."
A: "Requirements explicitly mentioned single-payment checkout; splitting was out of scope."
M: "If needed, Payment class could evolve to handle multiple payment methods via PaymentCollection."
P: "Future enhancement; design accommodates extension."
Using LAMP proactively ('Let me note a few limitations...') is more powerful than reactive use ('You asked about X, here's the limitation...'). Proactive limitation acknowledgment demonstrates confidence and thorough thinking.
The language you use to describe limitations matters enormously. Limitations sound like failures. Trade-offs sound like decisions. Reframe whenever possible.
| Limitation Framing ❌ | Trade-off Framing ✅ | Why Better |
|---|---|---|
| "It doesn't handle concurrency" | "We traded distributed complexity for single-threaded simplicity" | Shows intentional choice |
| "Performance degrades at scale" | "We optimized for implementation speed; scaling would need iteration" | Acknowledges priorities |
| "There's code duplication" | "We chose explicit over DRY to improve readability in early version" | Justifies the present state |
| "It can't support new vehicle types easily" | "We traded extensibility for type-safety in vehicle modeling" | Explains the axis of optimization |
| "Error handling is incomplete" | "We focused happy-path first; error scenarios are on enhancement list" | Shows systematic approach |
Structure: "We [traded / chose / prioritized] [what we gave up] for [what we gained], because [justification linked to requirements or context]."
Examples:
"We chose simplicity over extensibility, because requirements indicate vehicle types are stable."
"We prioritized time-to-solution over performance optimization, understanding that scale concerns are future considerations."
"We traded O(1) deletion for O(1) lookup, because our access patterns are read-heavy."
This language positions you as a decision-maker, not a defect-producer. You're not apologizing for flaws—you're explaining engineering choices.
Whenever you identify a limitation, ask: 'What did we gain by accepting this?' Memory-intensive? Probably faster. Complex? Probably more flexible. Rigid? Probably simpler. Finding the complementary benefit enables trade-off framing.
Identifying limitations is necessary but not sufficient. Senior engineers also suggest how limitations could be addressed. This demonstrates forward-thinking and solution orientation.
| Category | Description | Examples | When to Propose |
|---|---|---|---|
| Defer | Acknowledge for later | "We'd address this in v2 if needed" | Low-priority limitations |
| Design for Evolution | Show the path to fix | "The interface allows us to swap HashMap for distributed cache" | Medium-priority, clear fix path |
| Add Guardrails | Prevent the limitation from causing harm | "We'd add validation to prevent invalid states" | When limitation could cause failures |
| Monitor | Detect when limitation bites | "We'd add metrics to alert when queue depth exceeds threshold" | Performance/scaling limitations |
| Document | Ensure future developers know | "ADR would capture why this isn't thread-safe" | Known-but-acceptable limitations |
Limitation: The ParkingSpotFinder uses linear search (O(n)).
Mitigation Options:
Defer: "For lots under 1,000 spots, linear search completes in milliseconds. We'd only optimize if we scale to larger facilities."
Design for Evolution: "The SpotFinder interface abstracts the search algorithm. We could inject an IndexedSpotFinder without changing consuming code."
Add Guardrails: "We could add a configuration check that warns if lot size exceeds recommended threshold for current algorithm."
Monitor: "We'd add timing metrics on findAvailableSpot() to detect if search time becomes problematic before users complain."
Document: "The README would note that the current implementation is O(n) and outline conditions for when upgrade is warranted."
In an interview, offering one or two mitigation strategies per major limitation demonstrates proactive engineering.
Not every limitation needs mitigation. Some are so minor that addressing them would be over-engineering. Part of senior judgment is knowing which limitations to accept without mitigation, which to monitor, and which demand proactive solutions.
A key senior skill is knowing which limitations are acceptable and which must be addressed. Use these criteria to evaluate:
When an interviewer asks if a limitation is acceptable, use this response structure:
"Given [the requirements as stated], I'd argue [whether acceptable]:
If the limitation affects [core requirement], no—we'd need to address it.
If it affects [future or optional requirement], it's acceptable for now with [mitigation plan] if requirements evolve."
This demonstrates that you evaluate limitations against requirements, not against an idealized perfect system.
Some candidates refuse to accept any limitations, endlessly optimizing. This signals perfectionism over pragmatism. Real engineering ships software with known limitations. The question is which ones and whether they're documented and understood.
When you mention limitations matters. Too early derails progress. Too late appears evasive. Strategic timing maximizes impact.
| Timing | Best For | Example Language |
|---|---|---|
| At the decision point | Major strategic decisions | "Before I commit to Strategy pattern, I should note it won't handle state-based transitions..." |
| During design summary | Overall design review | "Let me summarize the design and then note key limitations and trade-offs..." |
| When asked | Response to interviewer probe | "You asked about scaling—good question. Here's where my current design would need evolution..." |
| Before code implementation | Pre-implementation review | "Before I code this, let me note what this implementation won't cover..." |
| End wrap-up | Comprehensive close | "To wrap up: here's the design, here's what it handles well, and here are known limitations for future work." |
Ideal limitation disclosure is integrated, not appended. Rather than:
"Here's my design... [10 minutes later] ...oh, and it has some limitations."
Consider:
"For vehicle types, I'm using inheritance. This gives compile-time type safety, though it means new types require code changes—which I believe is acceptable given the stated requirement that vehicle types are fixed. If that changes, we'd need to refactor to composition."
Notice how the limitation is woven into the decision explanation, not treated as an afterthought.
A powerful moment for limitations is the design-complete transition: 'Before we move to implementation, let me summarize the key decisions and their trade-offs.' This natural pause invites discussion and shows comprehensive thinking.
Acknowledging limitations is a senior skill that paradoxically increases confidence in your design. Let's consolidate the key principles:
What's Next:
Static designs face dynamic reality. The next page covers Adapting to New Requirements—how to gracefully handle changes, extensions, and curveballs thrown by interviewers, demonstrating the flexibility and resilience that marks senior engineering practice.
You now possess the frameworks and language for productively discussing design limitations. This transparency paradoxically increases interviewer confidence in your designs—because it proves you've thought deeply about what you're building and understand where it might struggle.