Skip to content
ExponaBLOG
ENGINEERING CRAFT · DAY 098 / 100

Choosing technology: a decision framework for production

How I pick tools for a new project.

Expona Admin8 min read

Choosing technology: a decision framework

After five years building production systems across e-commerce, CRM, trading and ride-hailing platforms, I've learned that picking the right technology isn't about finding the fastest, most popular or most feature-rich tool. It's about making a deliberate choice that fits your specific constraints and survives the first 18 months of maintenance. In this article, I'll walk you through the decision framework I use when starting a new project.

Start with constraints — They filter your options ruthlessly

The biggest mistake I see is starting with technology preferences and then trying to fit them into a project. I do the opposite: I list hard constraints first, and only then look at what tools can live within those boundaries.

Constraints typically fall into four categories:

  • Team skills. If your team knows React and TypeScript, shipping a frontend in Elm will train people faster than moving it forward. Five developers working with their strength beats three developers fighting a learning curve.
  • Timeline. A six-week MVP has different tools than a two-year platform. Choosing Django for a quick proof-of-concept when your team is .NET-first will burn runway.
  • Budget. Managed services cost more than self-hosted, but self-hosted requires ops headcount. A seed-stage startup and a Series B company have different math.
  • Compliance. Finance, healthcare and payments have regulatory requirements that eliminate entire categories of tools. You can't choose a SaaS database if GDPR says data must stay in-region.

Let me show you how this played out on a recent project. We were building a real-time trading dashboard for a Southeast Asian fintech. The constraints were:

  • The team was fluent in .NET and React, not Go or Rust.
  • We had eight weeks to launch an MVP.
  • The main regulatory requirement was audit logging—every trade had to be immutable.
  • We had budget for managed services but not for full-time DevOps headcount.

Those constraints immediately eliminated serverless backends (slow iteration cycle with .NET), Kubernetes (we didn't need the complexity), and any homegrown database (audit logging was easier in PostgreSQL with immutable event tables). We chose NestJS for the backend API, React for the frontend, PostgreSQL for events, and AWS RDS for managed operations. Not because they were trendy—because they matched our constraints.

Prefer proven tools — Maturity, community and hiring matter more than benchmarks

Once constraints filter your list, the next rule is simple: pick tools with a five-year track record of production use, not tools that benchmark well in a GitHub README.

When I evaluate a technology, I look at:

  • Maturity and stability. Has it been running production systems for at least five years? Are breaking changes rare? PostgreSQL, React, .NET and Kafka all meet this bar. Bleeding-edge frameworks do not.
  • Community size and depth. Can you find answers to weird bugs on Stack Overflow? Will you find developers at hiring time? A framework with 50,000 GitHub stars but 200 Stack Overflow questions answered per month is riskier than one with 10,000 stars and 2,000 questions answered.
  • Long-term viability. Is the tool backed by a company with revenue incentive to maintain it? Is it open-source with a healthy governance model? A tool maintained by one person's weekend project is not a foundation for a platform that needs to last five years.
  • Hiring pool. If a core developer quits, can you replace them? If you're building a team, will candidates have relevant experience? This matters more than technical purity.

A concrete example: I once evaluated a graph database for a recommendation engine on a travel platform. The database had faster query times on our specific workload than PostgreSQL. But it had a tiny community, hiring for it would be brutal, and the company behind it had precarious funding. I recommended PostgreSQL with denormalized JSON columns instead. Slower on benchmarks, but we shipped on time and hiring was straightforward.

This preference for maturity is sometimes called boring technology, and that's fine. I'd rather be bored and shipping than exciting and debugging framework internals.

Prototype the risky part — Test the hardest requirement before committing

Constraints and maturity narrow your choices. But there's usually still a decision to make: Do we use tool A or tool B? Both are proven, both fit our timeline, both have communities. This is where prototyping the riskiest part of your system breaks ties.

The risky part isn't always the hardest to build—it's the part where you're most uncertain about whether a tool can do the job. For the trading dashboard, the risky part was WebSocket performance at scale. We needed sub-100ms latency for price updates to 1,000+ concurrent traders. I didn't debate this in design docs. I built a two-week prototype:

  • Spun up a basic NestJS server with Socket.io.
  • Wrote a Kafka consumer that fed price updates into memory.
  • Ran load tests with 2,000 concurrent clients sending 10 updates per second.
  • Measured latency percentiles.

The prototype confirmed Socket.io could hit our latency targets with proper Redis adapter for horizontal scaling. It also revealed that naive broadcasting would saturate the server—we needed topic-based subscriptions. This learning happened in week two, not week seven.

The prototype didn't end up in production. But it answered the go/no-go question faster than any architecture review could. If the latency had been unacceptable, we would have known to explore gRPC or raw WebSockets instead. That's the point: validate your riskiest assumption before you commit the team to three months of building.

Record the decision — An ADR explains the choice to future teammates

After you've decided, write it down. Not in a Slack thread or a design doc that sits in a folder. Write an Architecture Decision Record (ADR) that lives in your repository alongside the code.

An ADR is simple. It answers five questions:

  1. What was the decision?
  2. What were the alternatives you considered?
  3. What was your context (constraints, assumptions)?
  4. What were the consequences—tradeoffs?
  5. What did you learn from prototyping or earlier implementations?

Here's a real ADR from that trading dashboard project:

typescript
// adr/001-realtime-price-updates.md
# ADR 001: Use Socket.io + Redis for real-time price updates

## Decision
We will use Socket.io with Redis adapter for distributing price updates to traders in real-time, rather than gRPC or raw WebSockets.

## Context
- Requirement: sub-100ms latency for price updates to 1,000+ concurrent traders.
- Team experience: strong in Node.js / NestJS, no gRPC expertise.
- Timeline: 8 weeks to MVP, no time for learning new frameworks.
- Budget: managed Redis on AWS is available.

## Alternatives considered
1. Raw WebSockets: Lower overhead, but requires custom connection pooling and broadcasting logic. Prototyping showed we'd need 4 weeks to build reliability features Socket.io provides out of the box.
2. gRPC bidirectional streams: Technically superior latency, but team would need 2-3 weeks to learn and no one in the market wants to hire gRPC specialists yet.
3. Server-Sent Events: Simpler than WebSockets but no true bidirectional messaging for trader commands.

## Consequences
- Positive: Socket.io is battle-tested, team ships faster, hiring is easier.
- Negative: overhead means we'll need horizontal scaling at 5,000+ concurrent users. Redis becomes a critical dependency.
- Learning: Prototype revealed that naive broadcasting wastes 40% of server CPU. We needed topic subscriptions from day one.

## Status
Accepted. Prototype validated latency targets.

Why write this? Because in six months, a new team member will ask "Why Socket.io?" and you can point them to the ADR instead of trying to reconstruct the decision from memory. When you hit a scalability problem at 3,000 users, the ADR reminds you why you didn't pick gRPC—and whether those assumptions have changed.

ADRs also create a paper trail. You're building institutional knowledge, not tribal knowledge. That matters when people leave.

Common mistakes

Treating benchmarks as law. A tool that's 10% faster in isolation might be 50% slower in your system because it forces architectural compromises. Prototype with your actual workload.

Optimizing for hiring before you have constraints. Yes, React is easier to hire for than Svelte. But if your constraint is "the frontend runs offline on a low-power device," React might be wrong. Solve for your constraints first.

Skipping the prototype because you're confident. Confidence is not data. I've been wrong about latency, memory usage and scaling limits more times than I'd like to admit. Prototyping the risky part takes two weeks and saves you eight weeks of technical debt.

Not updating ADRs when assumptions change. If you hit that 3,000-user scaling problem and decide to migrate to gRPC, update the ADR to record the decision and the learning. Future you will be grateful.

Over-engineering for scale you don't have yet. Choosing Kafka because "we might need it" when you have 50 events per second is adding risk. Choose PostgreSQL, prove that it's a bottleneck, then migrate. Your team moves faster with less infrastructure to operate.

Key takeaways

  • Start with constraints—team skills, timeline, budget and compliance—not with technology preferences. Constraints filter ruthlessly and prevent expensive mistakes.
  • Prefer proven, mature tools with healthy communities over cutting-edge frameworks. Maturity, hiring pool and long-term viability matter more than benchmarks.
  • Prototype the riskiest technical assumption before committing the team. Two weeks of prototyping can save eight weeks of rework.
  • Record your decision in an ADR that lives in your repository. Future teammates, new hires and your future self need to understand the context and tradeoffs.
  • Revisit your decisions when assumptions change. Technology choices are not permanent, but the reasoning behind them should be documented.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles