Build, Library, SaaS or Managed Service
Four options rather than two, sorted by how much of the thing you end up owning. The question that decides it is whether this capability is what your product is judged on.
The requirement, the obvious build, and why it breaks
Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind survives until the requirement changes.
We need a capability that thousands of companies also need. Which parts of it should we own, and what are we agreeing to run at 3am?
Enterprise customers require single sign-on — SAML for two of them, OIDC for the rest — and one of them wants directory sync so that leavers lose access automatically.
SAML is a specification. We are engineers, we can read a specification, and it keeps us free of vendors and per-user pricing.
The specification is not the work. The work is XML signature validation, canonicalisation, clock skew, IdP-specific quirks and the four ways each of those fails open rather than closed — and failing open in an authentication path is a breach, not a bug.
- The specification is not the work. The work is XML signature validation, canonicalisation, clock skew, IdP-specific quirks and the four ways each of those fails open rather than closed — and failing open in an authentication path is a breach, not a bug.
- It is also permanent. Every enterprise customer arrives with a slightly different identity provider, so what looks like a project becomes a queue of integration tickets that never ends (The Complexity Budget).
- Nobody on the team has done it, so the estimate is a guess in the area where guesses are worst, and the failure is silent — a subtly wrong signature check passes every test you thought to write.
- The opposite mistake is just as available: buying a per-user SaaS for the whole user base to serve four enterprise customers, and discovering that the pricing scales with the free tier rather than with the revenue it unblocks.
What limits the solution, and what must never stop being true
This domain leads with these two. A design that ignores its constraints is not a design, and an invariant nobody named is one nothing is protecting.
- Four engineers, none of whom has implemented SAML before. One deal worth roughly a fifth of annual revenue is blocked on it.
- The company is in a regulated-adjacent market: a security incident in the login path is existential, and customers audit it.
- There is a budget for tooling, but per-monthly-active-user pricing on the whole user base would exceed the revenue from the deals it unblocks.
- A user who is removed from the customer's directory loses access, within whatever window was promised.
- Authentication never becomes unavailable because of a change to something that is not authentication.
Who owns what, and where the seams fall
Responsibilities decide boundaries; boundaries decide what an interface has to say.
- The team owns the parts of identity that are specific to the product: what a role means, what a session means, what happens on first login for an unknown user.
- A vendor or library owns the protocol — signatures, metadata, the IdP quirks — because that part is identical for everyone and expensive to get wrong.
- Somebody has to own the seam between them, and that ownership is the durable decision here regardless of which option is chosen (Anti-Corruption Layer).
- Put the boundary at "an authenticated identity arrives" rather than at the vendor's SDK. The rest of the system should not know whether the assertion came from SAML, OIDC or a password (Boundary Adapters).
- That boundary is what makes the decision reversible later, and it costs about a day. It is the highest-return part of this entire lesson.
- Do not put the boundary at the vendor's user model. Adopting someone else's idea of a user, an organisation and a role is how a login integration ends up owning your domain (Anti-Corruption Layer).
Four options, sorted by what you end up owning
The question is usually posed as build or buy, which hides the two options in the middle where most good answers live. Sorting them by what you own — the code, the operation, the data, the roadmap — makes the comparison concrete.
For this requirement the differentiation question does most of the work. Nobody buys the product because of its login, and a mistake there is existential, which points hard at not owning the protocol.
Is this capability what customers judge us on, and what are we prepared to be paged for?
when The capability is differentiating, or the requirements are genuinely unusual, and the expertise exists in-house.
cost A permanent queue of identity-provider integrations, a security surface with silent failure modes, and no external party to escalate to. Rarely right for a protocol like this at this size (Designing for Security).
when You need the data to stay in your systems, the protocol is stable, and someone will own upgrades.
cost You run it and patch it. No runtime third party and no per-user pricing — in exchange for a supply-chain obligation and an upgrade cadence somebody has to actually keep (Dependency Management).
when Speed matters most, the pricing model matches how your revenue scales, and an external dependency in authentication is acceptable.
cost Their outage is your outage, their pricing changes are your budget, and their user model will try to become yours. Fastest to the unblocked deal by a wide margin (Designing for Failure).
when Your cloud provider already offers it and you are already dependent on that provider anyway.
cost Less lock-in than a specialist SaaS in one sense and more in another — it deepens the provider relationship, and the behaviour is configured rather than coded, which is a different debugging experience.
when Revenue is blocked today and it is not yet clear which parts of identity matter to the product.
cost Requires the boundary on day one — an interface you own, with the vendor behind an adapter. Without that, "later" turns into a data migration and never happens (Anti-Corruption Layer).
The axes, for this decision
Six considerations recur across every build-or-buy question: whether it differentiates, what it costs to operate, how locked in you become, what the security exposure is, how the cost scales, and whether the expertise exists. They are not equally weighted, and which one dominates is the actual decision.
Here, security exposure and operational burden dominate and everything else is secondary — which is why the option that looks worst on lock-in still wins.
| Option | Simplicity | Flexibility | Operational | Migration cost | Note |
|---|---|---|---|---|---|
| Build it | Complete freedom and complete ownership, including of every IdP quirk and every signature-validation subtlety, forever. Migration scores well only in the trivial sense that there is nothing to migrate away from. This is the option whose true cost arrives in year two. | ||||
| Self-hosted library | Data stays in-house, no runtime third party, no per-user pricing. You own patching a security-critical dependency on someone else's release schedule, which is a real obligation and is frequently promised and not kept. | ||||
| SaaS in the login path | Live in days, and the deal unblocks. Their availability becomes yours, their pricing model becomes a budget line that scales with users rather than with the revenue this unblocks, and their user model will leak unless a boundary stops it. | ||||
| Cloud provider managed service | Cheap if you are already all-in on the provider, and one less vendor relationship. Configuration rather than code, which is faster to set up and considerably harder to debug when an assertion is rejected for a reason the console does not explain. |
caveat Higher is better on every axis, and none of this was measured — these are judgements about one team's situation, where a blocked deal and an unfamiliar security protocol dominate everything else. Three things the digits cannot express: that the failure mode of the build option is a silent authentication bypass rather than an outage, which is not comparable to any other cell in this table; that the SaaS row's migration mark improves sharply if a boundary adapter is written on day one, so that cell is a property of your design rather than of the vendor; and that all four rows change completely at a hundred engineers with a million users, where per-user pricing alone rules out the third row (Reversible and Irreversible Decisions).
How this decision goes wrong afterwards
Most of the damage from build-or-buy decisions is not in the choice; it is in the integration. A bought capability that has been allowed to spread through the codebase is worse than either endpoint, because you have the vendor cost and the coupling.
Every row below is preventable by the same one-day investment: an interface you own, with the vendor or the library behind it, and your own model on your side of it.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| The vendor SDK is called directly from feature code | Switching vendors is quoted as a quarter of work | No boundary: thirty call sites and a foreign key on their user id | Adapter on day one, your own identity type on your side. Retrofitting it later is possible but is now a data migration (Boundary Adapters). |
| Vendor changes pricing at renewal | A budget problem that engineering is asked to solve in six weeks | Cost scales with a metric unrelated to the revenue the capability unblocks | Check the scaling metric before signing, and keep the exit cheap. This is the vendor risk that most reliably materialises (Revisit Triggers). |
| A hard requirement the vendor cannot express | Custom code around the vendor, while still paying for it | The differentiating part was inside the bought capability, not outside it | Split the capability: buy the protocol, own the policy. Roles and permissions are usually yours even when authentication is not. |
| The built version meets its first unusual IdP | A two-week integration for one customer, then another | The long tail was never in the estimate | Price the second year before building. If the answer is a permanent queue of tickets, that is the decision (The Complexity Budget). |
| The self-hosted library goes unmaintained | A published vulnerability in the login path and no upstream fix | Patching obligation accepted in principle and not resourced | Name the owner and the watch mechanism when the library is chosen, or treat it as a build (Dependency Management). |
How to build it
Most important first.
- Ask first whether the capability is differentiating. Customers do not choose this product for its login page, so the goal is correctness and speed, not ownership (When Design Does Not Pay).
- Enumerate all four options, not two. Library — you run it, someone else wrote it. SaaS — someone else runs it and holds the data. Managed service — someone else runs the infrastructure, you configure the behaviour. Build — you own all of it.
- Price the ongoing burden, not the integration. Who patches it, who is paged when it breaks, who handles the next IdP quirk, and who reads the vulnerability feed — none of which is free.
- Check how the cost scales against how the revenue scales. Per-user pricing across a free tier and per-enterprise-connection pricing are wildly different bets for the same product.
- Design the exit at the same time as the entry. A vendor boundary with an interface you control turns a migration into a re-implementation of one adapter (Versioned Interfaces).
What the next change costs
The field this whole domain exists for. A structure is only better if it makes the change after this one cheaper — and it is worth saying which changes it does not help.
- With a boundary at "an authenticated identity arrives": adding an IdP is a configuration change; switching vendors is one adapter, roughly a week, plus a migration of stored identifiers.
- Without it: the vendor's SDK is called from thirty places and its user id is a foreign key in four tables, so switching is a data migration and a rewrite of the session layer (Data Migration).
- Building: every new enterprise customer is an engineering ticket rather than a configuration change, permanently. That is the number to put in front of the decision, because it is the one that recurs.
- Buying moves an engineering problem into a procurement and vendor-management problem, which is cheaper but is not nothing, and it is usually work that lands on the same engineers.
- A well-drawn vendor boundary costs a day now and constrains the design slightly forever, because you cannot use the vendor's richer features without punching through it.
- Choosing a library over a service keeps the data in-house and puts the patching obligation on a team that may not be watching for it — which is a security trade in both directions rather than a straightforward win.
What can go wrong
- The vendor's model leaks into the product: their organisation becomes your tenant, their roles become your permissions, and the migration cost triples (Leaky Abstractions).
- The build option is chosen for cost reasons and the true cost — the queue of IdP integrations — appears a year later as an unbudgeted permanent workload.
- A vendor is chosen and then a hard requirement arrives that it cannot express, and the team builds around the vendor while still paying for it.
- Pricing changes. This is the vendor risk that is most often ignored and most often realised, and the exit is only cheap if the boundary was designed on day one.
- A SaaS in the login path is a runtime dependency on someone else's availability, and its outage is indistinguishable from yours to a customer (Designing for Failure).
- A library is a build-time dependency with an upgrade obligation and a supply-chain surface, but no runtime third party (Transitive Dependencies).
- Building creates a dependency on continued in-house expertise, which is the least visible and least transferable of the three (Bus Factor).
- "Buying is always faster." Integration is fast; the boundary, the data model reconciliation and the negative-path testing are not, and they are where the time goes.
- "Building keeps us in control." It gives you control of the code and hands you an obligation. Control that you have to be paged for is a cost centre, not an asset.
- "It is only a few weeks of work." For a protocol with a queue of third-party integrations behind it, the initial build is the small part. Price the second year (The Complexity Budget).
- "Open source means no vendor risk." It moves the risk from pricing to maintenance: an unmaintained library in the login path is a worse position than a vendor invoice (Dependency Management).
Testing it, and how it ages
- Test your side of the boundary with a fake identity provider. If the login flow can only be tested against a live vendor account, the boundary is in the wrong place (Test Doubles, Precisely).
- Test the negative case explicitly: a revoked user, an expired assertion, a signature from the wrong key. Authentication code is the archetype of a place where the happy path passing means nothing (Designing for Security).
- For directory sync, test the removal path end to end. Removal is the requirement customers audit and the one nobody exercises (Least Privilege as a Design Decision).
- Buy decisions age with the vendor: pricing changes, acquisitions, deprecations and end-of-life all arrive without your input, which is the tax for not running it (Deprecation).
- Build decisions age with the team. The system stays correct exactly as long as somebody understands it, and that person eventually leaves.
- The common trajectory is buy first to unblock revenue, then build the parts that turn out to be differentiating once you know which those are — and that trajectory is only affordable if the boundary was drawn on the first day.
Where this applies
This domain's advice is contested more than most. These labels say what each claim is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view rather than a caricature.
- GENERALThe four-option framing and the differentiation question apply to any capability many companies need — payments, search, email, feature flags — though the security weighting used here is specific to the login path and would be much lower for, say, a CSV parser.
- SCALE-SPECIFICAt four engineers, buying almost always wins because the operational burden is the binding constraint. At a scale where a dedicated identity team exists, the calculation inverts: per-user vendor pricing across millions of users exceeds a team's salary, and the expertise to run it in-house is available. Advice from either end is wrong at the other.
- CONTESTEDThe strongest opposing view is that "do not build undifferentiated things" has been over-applied to the point where teams assemble products out of a dozen vendors, each with its own outage profile, data model and renewal negotiation — and that the integration and vendor-management cost of that portfolio exceeds what building two or three of them would have cost, while making the product a hostage to other companies' roadmaps. That case is strongest for capabilities that are simple, stable and central to the product's data model, and weakest for security protocols with a long tail of third-party quirks.
Where the depth lives
This domain teaches the codebase-level structure and hands the rest off.
- — System Design — a bought capability in the request path is an additional availability dependency, and the composition of availabilities is the part that build-or-buy discussions almost never include.