Practical Coding Rounds/Machine-Coding Execution Speed

Designing for Extension Without Over-Engineering

The specific balance this round grades that a take-home doesn't: enough abstraction that the obvious next feature (a new payment type, a new vehicle type) doesn't require a rewrite, without speculative interfaces built for requirements nobody asked for.

!!!3/5Theory: 20m

Two ways to fail this round's design half

Machine-coding prompts almost always come with an explicit or implied "and this should be easy to extend with X later" — a new vehicle type, a new payment method, a new notification channel. There are two distinct, opposite ways to answer that badly: hardcode everything, so the obvious next feature requires rewriting several existing classes, or over-engineer speculatively, building a generic, configurable framework for extension points nobody asked for, at the cost of time you needed for the actual core functionality. Both read as missing the point, just in opposite directions — the skill being graded is judging how much abstraction a specific, named future requirement actually justifies, not "more abstraction is always better" or "abstraction is always premature."

YAGNI, applied to a prompt that names its own future requirements

"You Aren't Gonna Need It" is usually invoked against abstraction built for imagined future requirements — but a machine-coding prompt is unusual in that it often states a near-future requirement directly ("assume we'll need to support motorcycles and buses later," "we may want a subscription-based pricing model eventually"). That changes the calculus: YAGNI's default skepticism toward speculative generality doesn't apply cleanly when the generality isn't speculative, it's explicitly requested. The judgment call becomes: build the cheapest abstraction that satisfies the stated future need, and stay skeptical of abstracting for anything beyond that. If the prompt doesn't mention multiple payment methods at all, building a pluggable payment-strategy interface anyway is exactly the over-engineering this subtopic warns about — however clean it looks in isolation, it's time spent on a requirement that was never asked for.

The cheapest extension points are usually enough

For the handful of extension shapes that come up constantly in this style of prompt — a new pricing rule, a new item/vehicle/user type, a new notification mechanism — the cheapest adequate tool is almost always small: a one-method interface (or, in languages with first-class functions, a plain function reference) that lets a new case be added as a new small unit, without touching existing code. This is the Strategy pattern in its simplest form, and it's worth building automatically, by habit, the moment a prompt mentions "different types of X" — but building it as a small interface, not a generic rule-engine, config-driven abstraction, or plugin-registration system unless the prompt's scope genuinely demands that level of flexibility (it almost never does in 60-90 minutes).

A concrete signal for "have I gone too far the other way"

If you notice yourself building a configuration format, a registration mechanism for that configuration, and a generic dispatcher that reads the configuration — for a feature the prompt asked for in one sentence — that's the over-engineering failure mode, and the fix is to delete it in favor of the plain interface-plus-a-few-classes version. A rough time-based check that works well under this round's constraints: if an extension point takes more than a few minutes to build, ask whether the prompt actually asked for that much flexibility, or whether a much smaller version would satisfy the same stated requirement.

Naming the trade-off out loud is itself part of the signal

Saying "I could build a fully generic strategy-configuration system here, but since the prompt only mentions two fee types, I'll use a simple interface with two implementations and can extend it if a third comes up" demonstrates the judgment explicitly, rather than leaving the interviewer to infer whether the choice was deliberate or accidental. This is a small habit, but it's the difference between "candidate built a simple solution" and "candidate consciously chose the simple solution over a more general one, for a stated reason" — the second is a substantially stronger signal from the same code.

Reference implementations in:

One Cheap Extension Point vs. a Speculative Framework

A parking-fee calculation that anticipates "more fee types later" (a stated or strongly implied future requirement in most parking-lot prompts) with one small interface — versus the over-engineered version that builds a configurable rule engine nobody asked for.

from abc import ABC, abstractmethod # Cheap, genuinely useful extension point: one small interface, one method. class FeeStrategy(ABC): @abstractmethod def calculate(self, hours: float) -> float: ... class HourlyFeeStrategy(FeeStrategy): def calculate(self, hours: float) -> float: return hours * 2.50 class FlatDailyFeeStrategy(FeeStrategy): def calculate(self, hours: float) -> float: return min(hours * 2.50, 20.00) # Adding a new fee type later is a new 3-line class, not a change to existing code.

This is the entire extension point: one abstract method, swapped in via composition. It's cheap enough to write in under two minutes and directly answers "what if we need a different pricing model" if asked — the over-engineered alternative (a generic rule-configuration DSL) would cost 20+ minutes to build for a question that might not even come up.

Further Resources (Optional)