Making Tech Stack Decisions: A Framework
A practical framework for evaluating and choosing technologies that will serve your team and business for years to come.
Making Tech Stack Decisions: A Framework
Choosing your tech stack is one of the most consequential decisions you'll make as a CTO. The right choice accelerates your team. The wrong one creates years of technical debt. Here's the framework I use to make these decisions systematically.
The Core Principle
Choose boring technology.
This doesn't mean old or outdated. It means proven, well-understood, and appropriate for your needs. Save your innovation budget for where it matters—your product.
The Decision Framework
Step 1: Understand Your Context
Before evaluating any technology, understand your constraints and requirements.
Business Context
- Stage: Pre-product? Scaling? Enterprise?
- Timeline: How quickly do you need to ship?
- Budget: What can you afford in time and money?
- Risk tolerance: Can you handle bleeding edge?
Team Context
- Size: 2 people or 50?
- Experience: What does your team already know?
- Growth: How fast are you hiring?
- Retention: How long do people stay?
Product Context
- Scale requirements: 100 users or 100 million?
- Performance needs: Real-time or eventual consistency?
- Data sensitivity: Handling payments? Health data?
- Compliance: GDPR? SOC2? HIPAA?
Step 2: Define Your Criteria
Not all criteria matter equally. Weight them for your context.
Must-Have Criteria (Deal-breakers)
1. Solves the actual problem
- Does it address your real need?
- Is it designed for your scale?
- Will it grow with you?
2. Team can execute with it
- Can your team learn it reasonably quickly?
- Is there good documentation?
- Are there people you can hire who know it?
3. Sustainable long-term
- Active maintenance and community
- Clear upgrade path
- Vendor stability (if applicable)
Important Criteria (Weight These)
Performance (10-30% weight)
- Latency requirements
- Throughput needs
- Resource efficiency
Developer Experience (20-40% weight)
- Learning curve
- Debugging tools
- Local development setup
- Testing capabilities
Ecosystem (15-25% weight)
- Library availability
- Tool integration
- Community support
- Hiring pool
Operational (15-25% weight)
- Deployment complexity
- Monitoring/observability
- Scaling characteristics
- Cost at scale
Risk (10-20% weight)
- Vendor lock-in
- Technology maturity
- Community sustainability
- Migration difficulty
Step 3: Evaluate Options
Use a structured scorecard to compare technologies.
The Technology Scorecard
Example: Choosing a Backend Language
| Criteria | Weight | Python | Go | Node.js | Java | |----------|--------|---------|-----|----------|------| | Team familiarity | 25% | 9 | 4 | 7 | 5 | | Performance | 15% | 5 | 9 | 7 | 8 | | Ecosystem | 20% | 9 | 7 | 8 | 9 | | Developer experience | 25% | 8 | 7 | 8 | 6 | | Hiring pool | 15% | 8 | 6 | 9 | 7 | | Weighted Total | | 7.85 | 6.65 | 7.80 | 6.90 |
The formula:
Score = Σ(Criteria Weight × Technology Rating)
Step 4: Run Small Experiments
Before committing, validate your assumptions.
The Spike
Time-box: 2-5 days
Build a realistic proof-of-concept:
- Solve a representative problem
- Deploy to production-like environment
- Measure what matters (performance, developer time)
- Document learnings
Questions to answer:
- Is it as good as the documentation claims?
- What surprised us?
- What hidden complexity did we find?
- Would we be happy using this daily?
Step 5: Make and Document the Decision
Use Architecture Decision Records (ADRs) to capture your reasoning.
ADR Template
# ADR-001: Choose PostgreSQL for Primary Database
## Status
Accepted
## Context
We need a primary database for our SaaS application handling:
- User accounts and profiles
- Product catalog
- Order management
- Audit logs
Requirements:
- ACID transactions
- Complex queries
- Scale to 1M+ records
- Team familiar with SQL
## Options Considered
1. PostgreSQL
2. MongoDB
3. MySQL
## Decision
We will use PostgreSQL for our primary database.
## Rationale
PostgreSQL scored highest on our evaluation:
- Strong ACID guarantees (critical for orders)
- Excellent JSON support (flexible schema where needed)
- Team has 3 years PostgreSQL experience
- Rich ecosystem (PostGIS, full-text search)
- Known scaling path to 100M+ records
## Consequences
Positive:
- Leverage existing team expertise
- Single database for structured and semi-structured data
- Strong tooling and extensions
- Clear scaling path
Negative:
- More operational complexity than managed NoSQL
- Vertical scaling limits (mitigated by read replicas)
- Learning curve for horizontal sharding (if needed)
## Notes
- Will use AWS RDS for managed PostgreSQL
- Plan to implement read replicas at 100k users
- Monitor query performance from day one
Category-Specific Guidance
Databases
Consider your access patterns first:
- Known schema, complex queries, transactions → PostgreSQL, MySQL
- Flexible schema, simple queries, horizontal scale → MongoDB, DynamoDB
- Time-series data → TimescaleDB, InfluxDB
- Graph relationships → Neo4j, PostgreSQL + extensions
- Caching → Redis, Memcached
The default choice: PostgreSQL. It handles 80% of use cases excellently.
Backend Frameworks
Consider team and ecosystem:
- Fast iteration, great libraries → Python (Django, FastAPI)
- High performance, simple concurrency → Go
- Full-stack JavaScript → Node.js (Express, NestJS)
- Enterprise, proven patterns → Java (Spring Boot)
The default choice: Whatever your team knows best. Framework choice rarely kills companies; poor execution does.
Frontend Frameworks
Consider application type:
- Complex SPA → React, Vue
- Content-heavy, SEO-critical → Next.js, Nuxt
- Internal tools → React Admin, Retool
- Mobile → React Native, Flutter
The default choice: Next.js (React). Best balance of capability and ecosystem.
Infrastructure
Consider your stage:
- Early stage (< 10 users) → Vercel, Netlify, Railway
- Growth stage (10-100k users) → AWS/GCP/Azure with IaC
- Scale stage (100k+ users) → Kubernetes, custom infrastructure
The default choice: Managed platforms until you feel the pain. Premature infrastructure complexity is wasteful.
Monitoring/Observability
Consider your needs:
- Getting started → Sentry, LogRocket
- Growing → Datadog, New Relic
- Advanced → Prometheus + Grafana, OpenTelemetry
The default choice: Start with your cloud provider's native tools, migrate when limits hit.
Common Mistakes to Avoid
1. Resume-Driven Development
Mistake: Choosing tech because it looks good on resumes. Fix: Choose for the problem, not the hype.
2. Over-Engineering Early
Mistake: Kubernetes on day 1. Fix: Start simple, add complexity only when needed.
3. Ignoring Team Context
Mistake: Choosing Rust when team knows Python. Fix: Team productivity trumps theoretical performance.
4. Technology Lock-in Without Realizing
Mistake: Deep AWS integration without considering alternatives. Fix: Abstract vendor-specific features or accept the trade-off consciously.
5. Chasing Benchmarks
Mistake: "Framework X is 50% faster!" Fix: Your bottleneck is rarely the framework.
6. Neglecting Operational Complexity
Mistake: Microservices with 3 engineers. Fix: Complexity should match team size and needs.
The Boring Technology Budget
Dan McKinley's rule: You get about 3 innovation tokens. Spend them wisely.
Example allocation:
- Boring: PostgreSQL, React, TypeScript, AWS
- Innovative: Real-time collaborative editing (your differentiator)
This gives you one area to innovate while keeping everything else stable and well-understood.
When to Revisit Decisions
Technology decisions aren't forever. Revisit when:
Clear pain points emerge:
- Performance doesn't meet requirements
- Team productivity significantly hampered
- Costs ballooning unsustainably
Context changes significantly:
- 10x growth in users or data
- New compliance requirements
- Major team composition changes
Better alternatives mature:
- New technology solves old problems better
- Ecosystem shifts significantly
The rule: Migration is expensive. Only switch when pain of staying exceeds pain of switching.
Migration Strategy
If you must migrate:
1. Incremental, Not Big Bang
- Migrate component by component
- Run old and new in parallel
- Validate each step
2. De-risk Early
- Start with non-critical systems
- Build confidence and learnings
- Document patterns
3. Plan for Rollback
- Keep old system running
- Feature flags for new system
- Quick rollback path
4. Measure Everything
- Before/after metrics
- Cost comparison
- Team productivity impact
The Decision Checklist
Before finalizing any technology choice:
- [ ] Solves the actual problem (not a hypothetical one)
- [ ] Team can execute with it (or learn quickly)
- [ ] Proven in production at our scale
- [ ] Active community and ecosystem
- [ ] Clear operational story
- [ ] Documented decision in ADR
- [ ] Tested with spike/prototype
- [ ] Stakeholders aligned
- [ ] Escape hatch identified (if needed)
Final Thoughts
The best technology choice is the one that:
- Solves your problem
- Your team can execute with
- You can maintain long-term
Everything else is secondary.
Remember: Technology is a means, not an end. The goal is building great products, not using cool technologies.
Default to boring. Save your innovation budget for your product, not your infrastructure.
When in doubt, choose the technology you'd want to debug at 2am on a Sunday.
What's your framework for technology decisions? What would you add or change?