Build vs buy vs glue: a decision framework for small teams. Run the differentiator test, price hidden integration and maintenance costs, and design for the swap.
"Should we build this ourselves or just pay for it?" I get some version of that question every few weeks, and I've stopped answering it directly, because it's the wrong question. The build-vs-buy decision has three doors, not two, and the third one is where most of my best calls have come from.
I've stood on both sides of this and picked wrong in both directions. I've built things I should have bought, and paid a vendor for things I could have glued together in an afternoon. At Shpper, with a small team in Dubai and a hard rule that infrastructure stays close to free, getting this call right is the difference between shipping and drowning in maintenance nobody asked for. So here's the decision framework I actually use now, assembled entirely out of scars — a practical build-vs-buy-vs-glue system for small engineering teams and early-stage startups.
The textbook version of build-vs-buy is a spreadsheet exercise. You estimate engineering cost, compare it to a vendor's annual price, factor in a discount rate, and pick the cheaper column. It's the kind of total-cost-of-ownership calculation that looks rigorous in a board deck.
It's also close to useless for a five-person startup, for three reasons.
First, it assumes your engineering time is fungible. It isn't. The week your best engineer spends building a billing system is a week not spent on the thing that makes you different. That opportunity cost never shows up in the spreadsheet, and it's usually the largest number in the whole equation. A senior engineer-week is not a line item you can buy back later at the same price.
Second, it treats "buy" as a finished decision. You don't buy software and walk away. You buy a relationship: an integration to maintain, a vendor whose priorities aren't yours, a pricing page that will change the quarter after you depend on it, and an SLA you'll re-read the first time something breaks.
Third, and this is the one that matters, the framework ignores the question that actually determines the answer: is this thing the reason customers pay you, or is it just something they expect to exist? Get that wrong and no amount of spreadsheet precision saves you. You can compute a flawless cost comparison and still make a strategically fatal decision, because you optimized the wrong variable.
So I throw the spreadsheet out as the first step and bring it back later as a tiebreaker. The first cut is strategic, not financial.
Before I estimate a single cost, I ask one question about the capability in front of me: if this were mysteriously twice as good as everyone else's, would a single customer care?
That's the differentiator test. It sorts almost everything into two piles.
The rule that falls out of this is almost embarrassingly simple:
Build your core. Buy or glue your table stakes.
Nobody switches to you because your login flow is elegant. They switch because the thing you're uniquely good at is uniquely good. Every hour spent hand-crafting table-stakes plumbing is an hour stolen from the only work that compounds into a moat.
The trap is that table-stakes work is fun. Auth is a satisfying puzzle. A homegrown feature-flag system feels like real engineering. A bespoke job queue scratches an itch. That's exactly why it's dangerous: it produces the pleasant sensation of progress while your actual differentiator sits untouched. Engineers gravitate to well-defined problems with clean edges, and table-stakes infrastructure is full of them. Your core, by contrast, is usually messy, ambiguous, and hard — which is precisely why it's defensible.
One more wrinkle: core isn't permanent. Something table-stakes today can become core tomorrow if you decide to compete on it. When we made onboarding speed a real selling point on one product, onboarding quietly moved from "buy it" to "own it." Re-run the test every couple of quarters, not once at the start. The map of core-versus-table-stakes drifts as your strategy sharpens.
When a capability comes up, I write two sentences on the whiteboard:
If a capability honestly fits both sentences, that's your early warning that it's a differentiator-in-waiting, and you should lean toward the "buy now, build later" path I describe below.
Buying feels like the safe, cheap, grown-up choice. Often it is. But the sticker price is the smallest part of what you're signing up for.
Integration is the real bill. The vendor's demo assumes a greenfield app that looks exactly like their example. Yours doesn't. You'll spend days mapping your data model onto theirs, handling the edge cases their SDK pretends don't exist, and writing the glue that makes their webhooks survive your retries. I've never once integrated a third-party service in the time the vendor's "5-minute quickstart" promised. Budget for idempotency, for retry logic, for the migration script when their schema assumptions collide with yours, and for the observability you'll bolt on because their dashboard doesn't answer the question you actually have at 2 a.m.
Lock-in is a slow tax. The deeper a tool sinks into your codebase, the more expensive it becomes to leave. Your data ends up in their schema. Their concepts leak into your domain language. Their identifiers become your foreign keys. Two years later, migrating off is a quarter-long project nobody wants to fund, so you don't, and you keep paying whatever they decide to charge. Vendor lock-in isn't a single event; it's compound interest on a decision you made when you weren't paying attention.
Their roadmap isn't yours. When you buy, you inherit a vendor's priorities. The feature you desperately need sits in their backlog behind twelve enterprise customers who pay a hundred times what you do. The bug that's a five-alarm fire for you is a "we'll look into it" for them. You've outsourced not just the code but the urgency, and urgency is the one thing a startup can't outsource.
None of this means don't buy. It means price the whole thing: integration time, exit cost, and your loss of control over the timeline. A useful gut check before you sign: if this vendor doubled their price tomorrow, or got acquired and sunset the product in six months, how bad is my week? If the honest answer is "catastrophic," you're not buying a tool, you're buying a dependency, and you should either negotiate an exit path up front or reconsider.
Building has its own seductive lie: that the cost is the initial build. It isn't. The build is the down payment. The mortgage is maintenance, and it runs for the life of the product.
When you build something, you own:
I learned this the expensive way with a homegrown image pipeline. Building it took a week and felt great. Over the next year it ate far more time than that in one-off fixes: a format nobody anticipated, a memory spike under load, an orientation bug that only showed up on certain phones, an EXIF edge case that rotated half a user's uploads sideways. A hosted service would have absorbed every one of those for a few dollars a month. I'd essentially hired myself as unpaid on-call for a problem that wasn't my product.
The honest question isn't "can we build this?" A good engineer can build almost anything. The question is "do we want to still be maintaining this in two years, when it's boring and forgotten and still breaking?" Maintenance is the invisible tax that turns a clever build into technical debt. If you wouldn't sign up to be on-call for it indefinitely, you're not really deciding to build it — you're deciding to build it and then quietly neglect it, which is worse than either building or buying on purpose.
Here's the door most teams walk straight past. Build and buy aren't the only moves. There's glue: wiring together a couple of small, dull, well-understood tools with a thin layer of your own code, and calling it done.
Glue is my default for table stakes, and it's the option the classic framework can't even see, because it isn't purely "build" or purely "buy." It's fifty lines of your code holding two commodities together.
Some real examples from things I've shipped:
A minimal glue queue is genuinely this small:
-- The entire "queue service": one table.create table jobs ( id bigserial primary key, kind text not null, payload jsonb not null, status text not null default 'pending', -- pending | running | done | failed attempts int not null default 0, run_after timestamptz not null default now(), created_at timestamptz not null default now());-- The worker claims one job atomically, so two workers never grab the same row.update jobsset status = 'running', attempts = attempts + 1where id = ( select id from jobs where status = 'pending' and run_after <= now() order by run_after for update skip locked limit 1)returning *;
That for update skip locked is the whole trick — it's the same primitive the fancy queue services are built on. You are not reinventing distributed systems; you're using a boring database feature that has been battle-tested for decades.
The reason glue wins so often for a small team:
Glue's cost is that you own the seam. That's real, but the seam is small and it's yours, which is a very different thing from owning an entire subsystem or an entire vendor relationship. The failure mode of glue is "this got popular and now I need something more robust" — and that's a good problem, because by then you have the usage data to buy or build the real thing deliberately.
Sometimes the right answer changes with time. Something that's table stakes at launch becomes core once you have enough customers and enough data to compete on it. The move there is buy now, build later — rent the capability while it's a distraction, take it in-house once it's a differentiator.
This only works if you plan the swap on day one. Otherwise "later" never comes, because ripping out a deeply embedded vendor is always more urgent-feeling to postpone than to do. The vendor becomes load-bearing precisely because you never built the seam that would let you remove it.
How I keep the door open:
PaymentGateway, and one adapter class talks to the actual provider. The vendor lives in exactly one file. This is the Adapter pattern doing exactly the job it was invented for — isolating a volatile dependency behind a stable interface I own.// The whole app depends on this interface, never on a vendor.abstract class SearchIndex { Future<List<Doc>> query(String q, {int limit = 20}); Future<void> upsert(Doc doc);}// Today: rent it. Swapping providers touches one file.class HostedSearch implements SearchIndex { /* vendor SDK */ }// Later, if search becomes core: build it, delete nothing else.class OwnSearch implements SearchIndex { /* our engine */ }That interface costs about ten minutes up front. It's the cheapest insurance you'll ever buy against a decision you're not ready to make yet. The discipline is to resist leaking vendor-specific types across that boundary — the moment their SearchResult class shows up in your UI code, the wrapper has failed and the lock-in is back.
Say I'm starting a product where the pitch is a smarter search over a messy domain. Three capabilities land on my desk on day one. Watch how the same test sends each one through a different door.
Auth → buy (through a wrapper). Auth is pure table stakes here. Nobody picks us for our login page, and rolling my own is a security liability I'd be maintaining forever — password hashing, session management, token rotation, account recovery, the whole minefield. I use a managed provider — Firebase Authentication, in my world, because it's free at our scale and I already trust it. But every call goes through my own AuthService interface, so the provider lives in one file. Buy, wrapped.
Billing → glue. Billing is table stakes too, but I refuse to hand-build a payments system and I refuse to over-adopt a heavyweight billing platform I'll fight for years. So I glue: the payment provider handles the money and the PCI-compliance nightmare, and a small amount of my own code maps their webhooks to my subscription model. The provider owns the hard, regulated part; I own the fifty lines that connect it to my domain. Glue.
Search → build (eventually). Search is the entire point of the product. It's core, by definition. But on day one I don't have the data or the usage to build anything better than a hosted service — so I rent it, behind that SearchIndex interface from the last section. The plan is explicit: when search quality becomes the thing we win on, we take it in-house and swap the adapter. Buy now, build later, by design.
Three capabilities, three completely different answers, all from one question asked honestly: is this the thing customers pay us for? The spreadsheet would have told me to just buy all three and move on. That would have left my one true differentiator rented from a vendor, permanently — the single worst outcome available, and the one a pure cost comparison walks you straight into.