Why Stack Decisions Matter More Than You Think
A technology stack decision made today will shape your engineering capabilities for the next three to five years. It determines who you can hire, how fast you can ship, what problems you'll face at scale, and how much your infrastructure costs.
Despite this, many stack decisions are made based on developer preference, industry trends, or whatever the founding engineer happened to know when the project started. These aren't bad starting points — but they're not sufficient for decisions with multi-year consequences.
This framework provides a structured approach to evaluating technology stack choices for new projects and major rewrites.
The Four Dimensions of Stack Evaluation
1. Team Capability
The single most important factor in a stack decision is whether your team — current and future — can work effectively with it.
A technology that your team knows well will always outperform a "better" technology that they're learning. Productivity differences between an experienced team and a learning team are typically 3-5x for the first six months. On a twelve-month project, choosing a stack the team doesn't know can effectively halve the delivered scope.
Key questions:
- How many current team members have production experience with this technology?
- How large is the talent pool for hiring? Can you fill a vacancy within a reasonable timeframe?
- What does the onboarding curve look like? How long until a new hire is productive?
- Does the technology have established patterns and conventions, or will the team be inventing its own?
2. Project Requirements
Different projects have fundamentally different technical demands. A real-time collaborative editing tool has different requirements from an e-commerce platform, which has different requirements from a data processing pipeline.
Compute characteristics — Is the workload CPU-bound (data processing, video encoding), I/O-bound (API servers, database queries), or memory-bound (in-memory caching, large dataset manipulation)? Different languages and runtimes optimise for different workload types.
Latency requirements — Does the application need sub-millisecond response times (trading systems), sub-second (web applications), or is batch processing acceptable (reporting, ETL)? This affects language choice, architecture patterns, and infrastructure decisions.
Concurrency model — How many simultaneous users or requests must the system handle? Node.js handles many concurrent I/O-bound connections efficiently. Go excels at CPU-bound concurrent processing. Java's thread model works well for complex enterprise workflows.
Data characteristics — What volume of data, what access patterns, and what consistency requirements? These drive database choices, which in turn influence the application layer.
3. Ecosystem and Tooling
A technology's ecosystem — its libraries, frameworks, tooling, and community — often matters more than the core language or platform.
Library coverage — Does the ecosystem have mature, well-maintained libraries for the capabilities you need? Authentication, database access, API clients, file processing, email sending — these are table stakes. If you'd need to build them yourself, the technology's other advantages may not compensate.
Framework maturity — Is there a dominant framework with established conventions (Rails for Ruby, Django for Python, Next.js for React)? Mature frameworks accelerate development by providing answers to common questions: project structure, routing, database access, testing patterns.
Developer tooling — Quality of IDE support, debugging tools, profiling tools, and build systems significantly affects daily productivity. A language with excellent tooling makes developers faster and happier.
Community health — Is the community growing, stable, or declining? Are questions answered quickly on forums and Stack Overflow? Are security patches released promptly? A declining community means fewer libraries maintained, fewer answers available, and a shrinking talent pool.
4. Long-Term Maintainability
Software spends most of its life being maintained, not built. The stack should support maintainability over years.
Type safety — Statically typed languages (TypeScript, Go, Rust, Java) catch categories of errors at compile time that dynamically typed languages discover at runtime. For long-lived codebases maintained by changing teams, this safety net has significant value.
Readability — Code is read far more often than it's written. Languages and frameworks that favour explicit, readable code over clever, concise code produce codebases that are easier to maintain.
Upgrade path — How does the technology handle major version upgrades? A technology with a strong commitment to backward compatibility (Go, Java) reduces the maintenance burden compared to one with frequent breaking changes.
Operational requirements — What does it take to run this technology in production? JVM-based languages need tuning. Interpreted languages need runtime management. Compiled languages need build pipeline infrastructure. Each has operational implications.
Applying the Framework: Common Scenarios
Web Application with Complex Business Logic
Recommended: TypeScript with Next.js or a similar full-stack framework, PostgreSQL for data.
TypeScript's type system handles complex business domains well. The JavaScript ecosystem's breadth covers most integration needs. Next.js provides server rendering, API routes, and deployment flexibility. PostgreSQL handles relational data with strong consistency guarantees.
High-Throughput API Service
Recommended: Go or Rust for the service layer, with PostgreSQL or a purpose-chosen database.
Go's goroutine model and compilation to a single binary make it excellent for API services that need to handle high request volumes with low latency. Rust offers even better performance but with a steeper learning curve. Both produce small, efficient deployments.
Data Processing Pipeline
Recommended: Python for transformation logic, with Apache Airflow or Temporal for orchestration, and a cloud data warehouse for storage.
Python's data processing ecosystem (pandas, polars, SQLAlchemy) is unmatched. Orchestration tools handle scheduling, retries, and monitoring. Cloud data warehouses provide scalable storage and query performance.
Mobile Application with Shared Backend
Recommended: React Native or Flutter for mobile, with a TypeScript API backend.
Cross-platform mobile frameworks have matured significantly. React Native benefits from the React ecosystem and allows code sharing with a React web application. Flutter offers stronger performance guarantees and a more consistent cross-platform experience.
Anti-Patterns to Avoid
Resume-driven development — choosing technologies because they're interesting to learn rather than because they're right for the project. This is the most common source of poor stack decisions.
Premature optimisation — choosing a complex, high-performance stack for a project that will serve hundreds of users. Start with productive technologies and optimise when — and if — performance becomes a constraint.
Monoculture — insisting on a single technology for everything. Different parts of a system may genuinely benefit from different technologies. A TypeScript web application with a Python data pipeline and a Go real-time service is a reasonable architecture if the team can support it.
Ignoring operations — choosing a technology without considering who will operate it in production. A Kubernetes-orchestrated microservices architecture is powerful but requires operational expertise that many teams don't have.
Making the Decision
Gather your team, work through the four dimensions for each candidate stack, and make the trade-offs explicit. There is rarely a clearly "best" choice — there are trade-offs that align better or worse with your specific situation.
Document the decision and its rationale. In two years, when someone asks "why did we choose this?", the answer should be available and understandable. An Architecture Decision Record is a simple, effective format for this.
The best technology choice is the one your team can execute on effectively, that meets your project's technical requirements, that has a healthy ecosystem, and that you can maintain for years. Everything else is secondary.