The most expensive code I have written was code I wrote "quickly." Not because it was complex, but because I wrote it with the intention of fixing it later. Years and multiple teams later, that code is still in production -- sedimentary layers of quick fixes, each one making the next change harder.
The industry treats technical debt as linear: a week of shortcuts equals a week of cleanup later. It is not linear. It is compound.
How Debt Compounds
Rushed code does not just create a cleanup task. It creates organizational drag that grows through four mechanisms: context decay (assumptions fade within weeks), dependency accumulation (new features build workarounds on workarounds), onboarding friction (new engineers spend days deciphering "temporary" solutions), and invisible opportunity cost (features you cannot ship because the codebase resists change).
I once found this comment chain in production:
# TODO: Temporary fix for the demo on 5/15/2021
# Will be replaced with proper implementation after launch
# Update 7/2/2021: Keeping this for now, works well enough
# Update 3/10/2022: Don't touch this, multiple features depend on current behavior
# Update 9/1/2023: Dear God why
That is the lifecycle of every "temporary" shortcut. Documentation does not fix architectural problems. It just explains why you are stuck with them.
The Reversibility Test
Not all shortcuts are equal. The useful distinction is reversibility.
Two-way doors -- decisions you can cheaply undo -- are fine to make quickly. A function signature, a UI layout, a config value only one service reads. Move fast on these.
One-way doors -- database schemas, public API contracts, security boundaries -- deserve deliberate design regardless of deadline pressure. These are the decisions that create compound debt when you get them wrong.
// One-way door: 500 lines of "temporary" code everyone is afraid to touch
class LegacyPaymentProcessor { ... }
// Two-way door: clean interface with a replaceable adapter
interface PaymentProcessor {
processPayment(payment: Payment): Promise<Result>;
}
class LegacyAdapter implements PaymentProcessor {
// Thin wrapper. Replace without touching callers.
}
The adapter pattern takes slightly longer to write. It is dramatically cheaper to evolve. You migrate one flow at a time instead of scheduling a rewrite that never happens.
The Velocity Illusion
There are two kinds of fast. The kind that looks impressive in a sprint demo, and the kind that lets you ship a major feature six months from now.
Teams optimizing for the first kind feel productive in the short term and slow in the long term. Teams optimizing for the second kind feel slower initially and build compounding velocity.
Speed is not about writing code quickly. It is about being able to change direction safely. Every shortcut that makes the next change harder is a tax on your future speed -- and the interest rate is higher than you think.