← all articles

A Shortcut With No Expiry Date Becomes The Architecture

A Shortcut With No Expiry Date Becomes The Architecture

The most dangerous shortcuts in software are not the obviously bad ones. Nobody builds real work on top of an obviously bad shortcut. The dangerous ones are the reasonable, well-intentioned, entirely defensible decisions made to keep a team moving, which quietly stop being temporary because nobody was ever assigned the job of noticing when they should end.

I watched this happen recently on a project where an upstream system that a new module depended on simply wasn't ready yet. Rather than block a whole team behind someone else's timeline, an engineer wrote a seeder that generated plausible-looking data locally — the kind of thing every competent developer has done a hundred times. It was the right call. Waiting idle for another team's API to exist would have been worse than inventing something to develop against in the meantime.

The trouble is that fake data has a shape. Once other people start building against that shape, the fake data stops being a convenience and starts being a specification nobody wrote down. Screens got built assuming certain fields would always be present. Business logic got layered on top of relationships the seeder had invented for its own convenience, not because they reflected anything true about the real system. Weeks later, when the actual upstream data started arriving, it didn't line up cleanly, and untangling which parts of the new module were built against reality versus built against a placeholder took real, deliberate effort — including an explicit conversation to say, out loud, that the workaround needed to stop being relied upon.

Nobody made a bad decision at any single point in that sequence. That is the part worth sitting with. The seeder was reasonable. Building a screen against the data available to you is reasonable. Extending that screen's logic a little further next sprint is reasonable. Every individual step made sense against what the person taking it could see. What was missing wasn't judgement — it was ownership of the boundary between "temporary" and "load-bearing," and a mechanism for someone to periodically ask whether that boundary had already been crossed.

This is the pattern I keep seeing whenever a shortcut has no expiry date attached to it: the absence of an ending is not neutral. A workaround left open-ended does not sit still waiting to be revisited. It attracts more code, because code has to be built on top of something, and whatever exists today is what's available to build on. Six months of drift can turn a two-day local convenience into an assumption baked through three layers of a system, at which point removing it is not a cleanup task, it's a small migration project with its own risks and its own review cycle.

The uncomfortable part is that the discipline required to prevent this looks, from the outside, like paranoia about something that was clearly a good idea. Asking "when does this stop being needed, and who checks?" the same day you write a seeder feels like getting ahead of a problem that doesn't exist yet. But that's exactly when the question is cheap to ask and expensive to skip. Once three other people have quietly built against your placeholder, the question stops being a five-minute conversation and becomes a retrospective exercise in archaeology — figuring out, after the fact, what was ever supposed to be real.

In practice, the fix isn't complicated, it's just rarely done because it doesn't feel urgent at the moment it matters. A workaround introduced to unblock development should be named as a workaround somewhere more durable than a Slack message or a comment only its author will read again — a ticket, a technical debt register, a line in the project's own documentation of its current architecture. It should have an owner, not in the sense of blame but in the sense of a specific person whose job includes noticing when the real dependency finally lands and flagging that the placeholder can now be retired. And it should be revisited on a cadence, not left to be rediscovered by whoever eventually trips over it.

There's a broader lesson in here about how teams accumulate architecture without ever deciding to. Most technical debt registers I've seen are full of things everyone already knew were compromises — the library nobody's upgraded, the query that doesn't scale, the config that should have been externalised. Those are visible debts. The kind of debt in this story is different: it doesn't look like debt while it's being created, because at the point of creation it genuinely is the sensible option. It only becomes debt retroactively, once enough has been built on top of it that reversing course requires effort nobody budgeted for.

That's also why I don't think this is really a technical problem, even though it shows up as one. It's a problem of attention. Teams are good at noticing when something is broken. They are much worse at noticing when something that was never meant to be permanent has, through nothing more dramatic than the ordinary accumulation of other people's honest work, become permanent anyway. The seeder in this story wasn't a mistake. The mistake, if there was one, was assuming that its temporary nature was self-evident and would stay that way without anyone checking.

If there's a habit worth building from this, it's a small one: whenever you reach for a workaround to keep moving — fake data, a stubbed integration, a manual step standing in for automation that hasn't been built yet — write down, at the moment you create it, what would have to be true for it to be safe to remove, and who's supposed to notice when that happens. Not because you distrust the shortcut. Because the shortcut can't distrust itself. It will sit there looking exactly as reasonable on the day it becomes a problem as it did on the day it solved one, and by then the only people who can tell the difference are the ones who wrote down the difference in advance.

Matthew Ratcliffe, software developer and architect, Ballarat
Senior Software Engineer & Architect

20+ years across the technology stack — from greenfield builds to brownfield rescues. Based in Ballarat, VIC, focused on AI, healthcare and high-risk data systems. Full resume →

Share your thoughts