Skip to content
← All posts

Build vs. Buy: When to Write Code and When to Use What Already Exists

6 min read
software-engineeringpatterns

One of the most expensive decisions in software engineering has nothing to do with algorithms or architecture. It is the decision to build something from scratch when a perfectly good solution already exists.

The build vs. buy question comes up constantly. Should you write your own authentication system or use Auth0? Build a custom payment flow or integrate Stripe? Roll your own database ORM or use an existing one? Every team faces these decisions, and getting them wrong can cost months of engineering time.

The economics of buying

Here is a simple example that makes the math clear. A company builds a subsystem, say an email delivery service. They spend 3,000 engineering hours on it. At $40 an hour, that is $120,000 in development cost alone. Then add testing, maintenance, bug fixes, and documentation. The real cost is probably closer to $200,000.

Now that company sells their email service to other businesses. Because they can sell it to thousands of customers, they price it at $500 per year. They only need 400 sales to break even on the initial investment, and everything after that is profit.

If you are a customer, you get a battle-tested email delivery service for $500 instead of spending $200,000 building your own. That is a 400x cost difference. Even if the purchased solution only does 80% of what you need, the savings are enormous.

This is why the software industry runs on purchased and open-source components. Almost nobody writes their own web server, database driver, or HTTP client from scratch. The economics do not make sense.

When buying wins

Buying (or using an existing open-source tool) is the right choice in most situations:

The problem is well-understood and solved. Authentication, payment processing, email delivery, file storage, search indexing. These are solved problems. Thousands of companies have built, tested, and refined solutions for them. Your homegrown version will be worse.

The solution is not your competitive advantage. If you are building a food delivery app, your authentication system is not what makes you special. Your matching algorithm, your logistics, your user experience are. Spend your engineering time on what differentiates you.

Maintenance is expensive. When you build something, you own it forever. Every bug is your bug. Every security patch is your responsibility. Every upgrade is your project. A purchased or open-source solution spreads that cost across thousands of users and contributors.

Time to market matters. Building from scratch takes months. Integrating an existing solution takes days or weeks. If you are in a competitive market, those months matter.

When building wins

There are situations where building custom is the right call:

The existing solutions do not fit. If your requirements are genuinely unique and no existing tool handles them, you have to build. But be honest with yourself here. "No existing tool handles this" is often "I did not look hard enough" or "I would have to adapt my requirements slightly."

You need deep control. If the component is at the core of your product and you need to modify its behavior in ways that no vendor supports, building makes sense. A search engine company needs to build its own search infrastructure. A database company needs to build its own storage engine.

Vendor lock-in is a real risk. If switching costs are high and the vendor could change pricing, deprecate features, or go out of business, owning the code gives you control. This is a genuine concern for critical infrastructure components.

The "buy" option is more expensive long-term. Some SaaS products charge per-use pricing that becomes extremely expensive at scale. If you are processing billions of events per month, a vendor charging per event might cost more than building your own pipeline.

The hidden costs of building

Teams that choose to build often underestimate the true cost. Writing the initial code is the easy part. The expensive parts are everything that comes after:

  • Testing. The purchased solution has been tested by thousands of users in production. Your custom version has been tested by your team for a few weeks.
  • Edge cases. The purchased solution has encountered and handled edge cases you have not even thought of yet. Timezone bugs, Unicode issues, race conditions, failover scenarios.
  • Documentation. Someone has to write docs for your custom system. Someone has to keep them updated.
  • Onboarding. Every new engineer has to learn your custom system. With a well-known tool, they might already know it.
  • Security. Security vulnerabilities in popular tools get found and patched quickly by a large community. Vulnerabilities in your custom code sit there until someone finds them, and that someone might be an attacker.

A good rule of thumb: multiply your initial time estimate by 3x to 5x to get the true lifetime cost of building and maintaining a custom solution.

How to make the decision

Here is a practical framework:

1. Define what you actually need. Write down the specific requirements. Not "we need a message queue" but "we need to process 10,000 messages per second with at-least-once delivery and a dead letter queue." Specificity makes the comparison fair.

2. Survey existing options. Spend a day or two researching. Open-source tools, SaaS products, managed services. Check if something covers 80% or more of your requirements.

3. Estimate the real cost of building. Include development time, testing, documentation, maintenance, and opportunity cost (what else could your engineers be building?). Multiply by 3x.

4. Estimate the real cost of buying. Include the subscription or license fee, integration time, training, and any customization needed to cover the gap between the tool's capabilities and your requirements.

5. Compare. If buying is cheaper, buy. If building is cheaper (rare), build. If it is close, buy. The tiebreaker should always go to buying, because teams consistently underestimate the cost of building.

The best engineers are not the ones who build everything from scratch. They are the ones who know which pieces to build and which pieces to borrow. Knowing the landscape of available tools is a skill in itself.

Build vs. buy in your own code

This principle scales all the way down to individual lines of code. Every programming language ships with standard library tools that solve common sub-problems. Using them instead of reimplementing from scratch is the micro version of build vs. buy.

In Python:

  • collections.Counter counts element frequencies in one line instead of a manual loop
  • heapq gives you a min-heap instead of building your own priority queue
  • collections.deque gives you O(1) operations on both ends instead of using a list and paying O(n) for pop(0)
  • itertools handles permutations, combinations, and product without custom recursion
  • bisect does binary search insertion instead of hand-rolling it

Each one of these is a "buy" decision. Someone else built it, tested it, optimized it, and documented it. Using it saves you time and reduces bugs.

The same logic applies to frameworks, libraries, and packages. Django's ORM exists so you do not write raw SQL for every query. React exists so you do not manually manage DOM updates. NumPy exists so you do not write matrix multiplication in pure Python.

Knowing what tools exist and when to reach for them is one of the most valuable skills a developer can have. It is the difference between spending a week building something and spending an afternoon integrating something.

The takeaway

Build vs. buy is not about pride or preference. It is about making the most effective use of limited engineering time. Every hour you spend building something that already exists is an hour you are not spending on what actually matters: the unique, hard, valuable problems that only your team can solve.

Default to buying. Build only when you have a clear, specific reason. And when you do build, build on top of existing tools and libraries whenever you can.

The same principle applies to learning. You do not need to figure out every algorithm pattern from scratch. The patterns have been identified, documented, and organized. What you need is a system to internalize them so you can apply them under pressure.

Related posts