It's tempting to pick a technology stack first and then fit your project into it. Many teams do it. They choose microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. because Netflix uses microservices, or GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. because it sounds modern, or Kubernetes because someone at a conference was enthusiastic about it. Then they spend the first three months fighting infrastructure instead of building features. Requirements analysis is the discipline of reversing that order.
The wrong way and the right way
Wrong order:
"Let's build with React, Next.js, TypeScript, Prisma,
PostgreSQL, Docker, Kubernetes, and GraphQL!"
... but what exactly are you building? For how many users? With what team?
Right order:
1. What problem are we solving?
2. Who uses it and how many?
3. What are our constraints (time, money, team)?
4. What's the simplest stack that meets those constraints?The four dimensions
Scale and traffic
How many users you expect, and what their traffic pattern looks like, determines whether you need anything beyond a single server.
| Users | Traffic pattern | Implication |
|---|---|---|
| < 1,000 | Steady | Basic VPS or serverless |
| 10,000-100,000 | Steady | Standard hosted database + app server |
| 100,000+ | Spiky | Auto-scaling, CDN caching |
| Millions | Variable | Architecture becomes a feature |
Team and skills
The best stack is often the one your team already knows. Learning a new framework under deadline pressure adds weeks and bugs.
What technologies does your team know well? How large is the team, and will it grow? If you plan to hire, how available is talent for your chosen stack? React developers are common; Elm developers are not.
Timeline and budget
A prototype due in three weeks has different constraints than a product expected to last five years. Speed of development and long-term maintainability pull in opposite directions, the right balance depends on where you are.
Hosting budget matters too: $5/month points you toward static hosting or free-tier serverlessWhat is serverless?A hosting model where individual functions run on demand and the platform handles all server management, scaling, and uptime for you.; $500/month opens up managed databases and auto-scaling services.
Technical requirements
Some product requirements directly constrain your architecture:
SEO required? → Server-side rendering (Next.js, Remix)
Real-time updates? → WebSockets or SSE
Offline support? → Service workers, local-first data
Mobile app needed? → Shared API layer, possibly React Native
Regulatory compliance? → Specific data residency, audit loggingWorking through an example
Say you're building a blog platform. Let's actually apply the framework:
- Public blog posts with good SEO → needs SSRWhat is ssr?Server-Side Rendering - generating HTML on the server for every request so users and search engines see fully formed pages immediately.
- 100-1,000 daily visitors → no exotic scaling needed
- Solo developer → pick familiar tech, minimize overhead
- $10/month budget → serverlessWhat is serverless?A hosting model where individual functions run on demand and the platform handles all server management, scaling, and uptime for you. or static hosting
- Ship in one month → no time for a complex setup
Every one of those constraints points the same direction: Next.js on Vercel's free tier, with a managed Postgres database. That's not a lazy choice, it's a correct one for these requirements.
The anti-patterns
Over-engineering is real and it kills projects. Here are the three most common forms:
Premature scaling: building for millions of users when you have zero. Your architecture should match your actual scale, not your aspirational scale.
Resume-driven development: choosing Rust, WebAssembly, or a brand-new framework because they look good on a CV, not because they solve a problem you have.
Analysis paralysis: spending weeks comparing React vs Vue, PostgreSQL vs MySQL, RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. vs GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching., without making any decision. Most of these choices don't matter much, pick one and start.
YAGNI
"You Aren't Gonna Need It" is a principle from Extreme Programming that still holds. Don't build internationalizationWhat is i18n?Short for internationalization (18 letters between i and n) - structuring your code so it can support multiple languages and regions. support for a product that launches in one country. Don't build microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. for a product with three users. Don't add a message queue for features that don't need async processing yet.
Decision checklist
| Question | Points toward |
|---|---|
| Team < 5, tight timeline | Familiar stack, monolith |
| Need SEO, public-facing | SSR framework |
| Budget < $20/month | Serverless, free tier hosting |
| Real-time features required | Node.js + WebSockets or Supabase |
| Unknown requirements, might pivot | Start simple, design for change |
| Large team, multiple squads | Consider service boundaries |
// Example: Decision tree for a new project
function chooseTechStack(requirements) {
const stack = {};
// Frontend framework
if (requirements.seo) {
stack.framework = requirements.teamKnowsReact
? 'Next.js'
: 'Remix';
} else if (requirements.simple) {
stack.framework = 'Vanilla JS or small library';
} else {
stack.framework = requirements.teamSize > 5
? 'React' // More devs know it
: 'Vue'; // Easier to learn
}
// Database
if (requirements.users < 1000) {
stack.database = 'SQLite'; // Simple
} else if (requirements.relationalData) {
stack.database = 'PostgreSQL';
} else if (requirements.flexibleSchema) {
stack.database = 'MongoDB';
}
// Hosting
if (requirements.budget < 10) {
stack.hosting = 'Vercel/Netlify (free tier)';
} else if (requirements.budget < 50) {
stack.hosting = 'VPS (DigitalOcean, Linode)';
} else {
stack.hosting = 'Managed services (AWS, GCP)';
}
// Type safety
stack.typescript = requirements.teamSize > 3 || requirements.longTerm;
return stack;
}
// Example usage
const myProject = {
seo: true,
teamKnowsReact: true,
users: 500,
relationalData: true,
budget: 15,
teamSize: 2,
longTerm: true
};
const stack = chooseTechStack(myProject);
console.log(stack);
// {
// framework: 'Next.js',
// database: 'SQLite',
// hosting: 'VPS (DigitalOcean, Linode)',
// typescript: true
// }