Build Fast and Shallow on Purpose

Every team optimizes for something, whether or not anyone says so out loud. Some optimize for reliability, some for speed to market, some for polish. Building fast and shallow is one of those choices, and on the right project, it’s a legitimate one.

On my current project, that’s exactly the choice that’s been made, and it comes with a cost I hear about directly. One developer put it plainly in a team retrospective. “The product should be more interesting and good than it is, because of short-sighted decisions.” That’s not a complaint about ability. It’s the sound of someone doing a version of the work they know is less than what they’re capable of, over and over, without a clear answer for how much less is acceptable.

That kind of frustration doesn’t come from a lack of ability. Deciding how much imperfection is acceptable on a given feature can absolutely be an engineering judgment. Our engineers hold themselves to a high bar by training and instinct, but they can flex into a faster, looser way of working when the moment calls for it. What they can’t do is flex correctly without knowing which mode they’re supposed to be in, and why. That gap, between being capable of the call and never being told which call to make, is where the fatigue comes from.

Building Toward a Sale

To understand why that gap exists in the first place, it helps to know what we’re actually building toward. Our client isn’t planning to run this product for the next decade. The goal is an exit, a sale to a buyer who sees enough there to acquire the company. That single fact shapes almost every tradeoff on my current project. A buyer evaluating an acquisition target is judging breadth and momentum in a short window, not depth earned over years of real use. Optimizing for that kind of first impression means speed and surface area matter more than most of us would default to on a typical build. Fast and shallow isn’t a shortcut here. It’s the actual strategy, chosen on purpose, for a real reason.

Two Speeds, No Instructions

That strategy played out in a recent meeting with our client, while we were reviewing a new dashboard built to track team velocity and contributions, pull requests, review depth, time to merge, that kind of thing. Partway through, the conversation turned to a framework for sorting subsystems into two categories. Fast but fragile. Deep but slow. Some parts of the product should move quickly and accept some breakage, because the point right now is learning fast. Others need more care up front, because the cost of getting them wrong compounds. The categories made sense. Nobody had actually said out loud, subsystem by subsystem, which one applied where.

That’s the gap I raised in the meeting. The team understands, broadly, that we’re optimizing for speed and that means MVP-level delivery. But in conversations I overhear, what’s fuzzy is what level of brokenness is okay on any given piece of work. Without that answer, the judgment call quietly gets passed down to whichever developer is holding the code at the time, and they spend extra hours in that uncertain space, building something without ever getting a clear answer about what’s acceptable to ship.

The Missing Context

That uncertainty makes more sense once you look at what a developer actually has in front of them when they hit that fork. They have the task, the code, and their own training. What they don’t have is the current product priorities, which shortcuts a buyer would actually notice, and which ones nobody will ever look at twice. That context doesn’t show up by working harder or reading the ticket more closely. It has to be handed to them, on purpose, before they start.

To their credit, our client didn’t leave it as an observation once I raised it. The near-term answer was blunt. Stop waiting for scheduled check-ins. If a developer hits that fork, the “is this too broken to ship” moment, come ask directly, in real time, even if it’s just a five-minute huddle or a message. Our client said they’d rather be interrupted than have someone burn hours guessing. I think that’s a real fix, and I don’t think it’s the whole fix. “Ask me” works if people remember to ask, and if the answer doesn’t depend on someone being available at the exact moment a developer hits the wall.

What Actually Helps

Someone with the authority to set direction should name the current phase out loud at the start of each milestone. That means naming what the phase is, what it includes, and which subsystems are fast but fragile right now versus deep but slow. That should be decided up front, before anyone starts typing, not discovered mid-task by whoever gets there first.

What This Takes

Building fast and shallow well takes more than good intentions. It takes explicit permission, stated out loud, not just assumed. A team also needs to know who actually holds the authority to set the broken threshold, whether that’s a delivery lead, a tech lead, a product owner, or the client themselves. It doesn’t need to be a formal role. It just needs to be someone, clearly identified, whose call it is.

Beyond that, the team needs enough of the bigger picture to see how their piece connects to everything else, not full context, but enough to know how the parts thread together. And they need artifacts, plans, specs, something written down, but those don’t need to be polished. A rough sketch works, as long as it exists and everyone can see it. Ideally, all of this gets set at the start of a project and gets revisited again at the start of each major milestone, since what counts as good enough on day one won’t always hold true three months in.

We’re not fully doing this yet, not as a consistent practice at the start of every milestone. But naming the gap out loud, in the room, with the person setting the strategy, was a real step. Our engineers can absolutely do fast and loose work well when the moment calls for it. They just need someone to tell them, clearly and in advance, that this is one of those moments, and why.

Conversation

Join the conversation

Your email address will not be published. Required fields are marked *