Concepts / Quality, testing and extensibility / Trade-offs and over-engineering

Trade-offs and over-engineering

When a pattern is a cost, and how to say so.

01The intuition

The problem it solves

Asked to write a function that checks whether a string has duplicate characters, a candidate who has just learned design patterns produces this.

from typing import Protocol

class DuplicateStrategy(Protocol):
    def has_duplicates(self, s: str) -> bool: ...

class SetStrategy:
    def has_duplicates(self, s: str) -> bool:
        return len(set(s)) != len(s)

class DuplicateCheckerFactory:
    @staticmethod
    def create(kind: str = "set") -> DuplicateStrategy:
        return SetStrategy()

class DuplicateChecker:
    def __init__(self, strategy: DuplicateStrategy) -> None:
        self.strategy = strategy

    def check(self, s: str) -> bool:
        return self.strategy.has_duplicates(s)

Four classes, one line of logic, and no second strategy anywhere in sight. The interviewer learns that the candidate can spell the patterns and cannot tell when not to use them. Over-engineering is a design fault like any other: it costs reading time, test surface and change effort, and it hides the one decision that mattered.

Build it one piece at a time

Step one: write the simplest thing that meets the stated requirement, and say its cost. Here that is one function and one sentence about it.

def has_duplicates(s: str) -> bool:
    return len(set(s)) != len(s)      # O(n) time, O(n) extra space

Step two: name the pressure that would justify more, and wait for it. If the interviewer adds "the strings are huge and memory is tight", the trade-off changes: sort in place or use a bitmap for a known alphabet. The second version is chosen by a stated constraint, not by habit.

def has_duplicates_low_memory(s: str) -> bool:
    seen = 0                           # one bit per lowercase letter
    for ch in s:
        bit = 1 << (ord(ch) - ord("a"))
        if seen & bit:
            return True
        seen |= bit
    return False                       # O(n) time, O(1) space, a–z only

Step three: when two real options exist, lay them side by side with the quantity that decides between them, choose, and say what would change your mind. That is the whole skill; the pattern vocabulary is just how the options are named.

OptionTimeExtra spaceWorks forPick when
set of seen charactersO(n)O(k) distinct charsany textdefault
sort then scanO(n log n)O(1) if in placemutable inputmemory is the limit
bitmapO(n)O(1)small known alphabetalphabet is fixed and memory matters

The same discipline applies to class design. A strategy interface earns its place when a second strategy exists or is named in the requirements; a factory earns its place when creation has rules; a singleton almost never earns its place. The question to ask before adding a layer is "what change does this make cheaper, and has anyone asked for that change".

Why it works

Every abstraction is a bet that a certain kind of change will come. A bet that pays is an extensibility point; a bet that does not is a tax on every reader forever. Saying the trade-off out loud turns the bet into a decision the interviewer can evaluate, which is what the round is for. The simplest version first also gives you working code early, which is what a change request will be measured against.

Where it breaks: under-engineering has a cost too. Hard-coding the second rule into the first, or a God class that does everything, fails the same test from the other side. The signal for adding structure is concrete: a second variant exists, a dependency reaches outside the process, or two parts change at different rates. Until then, a function and a clear sentence beat a hierarchy.

value of an abstraction ≈ (probability the change comes) × (effort it saves) − (effort every reader spends on the indirection)

The trade-off you must say out loud

Say the simple design and its limits in the same breath: "a set, O(n) space; if memory is the constraint I would switch to a bitmap for a fixed alphabet." Then stop. Interviewers rate the ability to choose over the ability to list, and a candidate who names the pressure that would justify more complexity shows both. The follow-up that usually comes next is the very pressure you named, and now the second design is a short step rather than a rewrite.

Where it shows up in the 100 Google problems

02Used in these problems