The two categories of architecture decisions
- Type 1: One-way doors. These are hard or impossible to reverse. Your primary database. Your authentication model. Your multi-tenancy strategy. Your API contract with external consumers. Get these wrong and you're looking at a rewrite, not a refactor.Type 2: Two-way doors. These are easy to reverse. Your frontend framework. Your CSS approach. Your CI/CD pipeline. Your logging library. Get these wrong and you spend a weekend migrating, not a quarter.Most startup engineers treat everything like a Type 1 decision and over-engineer. Or worse, they treat Type 1 decisions like Type 2 and under-invest.Here's my breakdown of what matters and what doesn't at each stage.
0 to 1,000 users: Optimize for speed, not scale
- Pick a boring database. PostgreSQL. Not because it's the best at any one thing, but because it's good enough at everything and you won't need to replace it until you're well past product-market fit. I've seen startups pick MongoDB because "we don't know our schema yet" and then spend months fighting data consistency issues. PostgreSQL with JSONB columns gives you schema flexibility without sacrificing relational integrity.
- Design your API contract carefully. Your API endpoints are a Type 1 decision the moment another team or client depends on them. Version from day one. Use `/api/v1/` in your paths. Make response shapes consistent. When you need to change a field, add the new one alongside the old one instead of renaming.
- Choose a monolith. I'm not being controversial for the sake of it. A monolith deployed as a single service is faster to build, easier to debug, simpler to deploy, and cheaper to run than microservices. You can always extract services later. You can't easily merge them back together.
- Whether you use REST or GraphQL (pick whichever your team knows)
- Your hosting provider (you'll migrate before it matters)
- Your state management library
- Your testing framework
1,000 to 10,000 users: The cracks appear
- 1. Add database monitoring. I use pg_stat_statements to find slow queries.
- 2. Add indexes based on actual query patterns, not theoretical ones.
- 3. Separate read-heavy reporting queries onto a read replica.
- 4. Introduce connection pooling if you haven't already.Authentication decisions start biting. If you rolled your own auth at the MVP stage, this is where it hurts. Password reset flows have edge cases. Session management has security holes. OAuth integration requests start piling up.My strong recommendation: migrate to a managed auth provider (Auth0, Clerk, Supabase Auth) before you hit 10K users. The migration is painful. It's less painful at 10K than at 100K.The monolith needs boundaries. You don't need microservices yet. But you need internal boundaries. Organize your code into modules with clear interfaces. The billing module talks to the user module through a defined interface, not by reaching into its database tables.This is the single most valuable architectural pattern for growing startups: modular monolith. You get the deployment simplicity of a monolith with the organizational clarity of services.
10,000 to 100,000 users: Extract strategically
The decisions that kill startups
- Premature microservices. A 5-person startup with 12 microservices and a Kubernetes cluster is not engineering excellence. It's engineering theater. The deployment complexity alone will consume 40% of your engineering bandwidth. Ship a monolith. Extract when it hurts.Wrong database for the access pattern. Using a document database for relational data (or vice versa). Using a graph database because "our data is a graph" when a SQL join table would work fine. Match the database to your actual query patterns, not your data model on a whiteboard.Shared mutable state in distributed systems. The moment two services write to the same database table, you've built a distributed monolith with all the downsides of both architectures. If you must extract services, each service owns its data. Period.Ignoring observability. You can't fix what you can't see. Structured logging, error tracking, and basic APM should be in place from week one. I've debugged production issues at 3 AM. The difference between a 15-minute fix and a 4-hour nightmare is always whether you can see what happened.Building "for scale" without measuring. I once reviewed a system where the team had implemented event sourcing, CQRS, and a custom message bus for an application with 200 active users. They spent 6 months building infrastructure. Their competitor shipped features. Their competitor won.
