The most common misreading of "we pick the stack per engagement" is that it means we have no opinion about anything. That isn't true, and it would be useless if it were. What it actually means is that defaults exist, and you have to argue us out of them.
This sounds like a small distinction. It isn't. It's the difference between a senior team and a contractor pool wearing a senior-team t-shirt.
A default is what we reach for when nothing tells us to reach for something else
That's the whole definition. It's not "the only thing we know". It's not "the thing we will ram into your project regardless of fit". It's the answer we give when the project hasn't yet given us a reason to answer differently.
A studio without defaults is a studio that re-litigates every project from scratch. Every kickoff turns into a two-week sky-is-blue debate about the relational store. Every cache decision becomes a roundtable. Every frontend conversation starts at "should we use HTML?" That isn't senior. That's perpetual junior, dressed up as open-mindedness.
Senior engineering is the opposite of that. It's a short list of strong defaults, applied without ceremony, and a long list of conditions under which we'd cheerfully drop them. Knowing both lists is what makes the team senior. Memorising one without the other is what makes a team rigid or a team rudderless.
So here is the short list, by category. We'll tell you these on the first call too, in less time than it took to read this paragraph.
Relational store: Postgres, almost always
Postgres is a swiss army knife. It is not the best relational engine at any single thing — there are columnar warehouses that beat it on analytics, embedded engines that beat it on single-process simplicity, and document stores that beat it on schema-less flexibility. What Postgres is best at is being good at many things at once: ACID transactions, JSONB, full-text search, partial indexes, listen/notify, foreign data wrappers, mature extensions, and a community that has seen every failure mode you're about to discover.
For roughly nine in ten of the projects we take on, that bundle wins. You don't have to be the best at any one thing if you're the right tool for almost everything the project will throw at you in the first two years.
We'd reach for something else in a few specific cases. Analytics-heavy workloads with billions of rows and read-only aggregations? DuckDB locally or a columnar warehouse remotely. A single-tenant embedded use case where shipping a server is overkill? SQLite, happily. A document shape that genuinely doesn't fit relational — irregular, deeply nested, schema-on-read by nature? Reluctantly Mongo, and we'd want a serious conversation about whether the irregularity is real or just unfinished thinking.
Those are real reasons. "I read a blog post" is not.
Queue and cache: Redis
Redis is the default cache, the default short-lived queue, the default rate limiter, the default lock service, the default pub/sub for small fan-out. It is fast, it is simple, it has known failure modes, and the operational surface is small enough that one engineer can own it without it eating their week.
We'd reach for a real broker — Kafka, NATS, sometimes RabbitMQ depending on the shape — when the project has event streams that need durable replay, fan-out at a scale Redis pub/sub will choke on, or strict ordering guarantees across partitions. Those are not most projects. When they are the project, we don't pretend Redis is enough; we just don't pretend it isn't, when it is.
Frontend: a typed component framework with first-class server rendering
We have a default here too. It's the framework that pays for itself the fastest on the kind of work we usually do: marketing surfaces, dashboards, internal tools, the front of a SaaS. Typed. Component-based. Server rendering as a first-class citizen, not an afterthought bolted onto a client-only SPA.
We'd switch to a different typed framework — same family, different brand — if the team inheriting the codebase already knows it better than ours. Hand-over fit beats author fit. Every time.
We would push back hard on a stack that picked a frontend framework because it was on the front page of Hacker News last week. That isn't a reason; it's a vibe.
Backend: Python or TypeScript by default, Go where contention is the actual constraint
Python and TypeScript cover most of what we get asked to build. Both have mature ecosystems, both are pleasant for senior engineers and approachable for the more junior ones the client may eventually hire, and both are fast enough for the workload of nearly every project that isn't a high-frequency trading desk.
We reach for Go when latency under contention is the actual constraint, not the imagined one. "We might one day need to handle ten thousand requests per second" is imagined. "Our current load-tested p95 doubles when we add a fifth replica" is actual. Don't pre-pay for performance you can't yet measure the lack of.
Deploys: containers, simple orchestration, no Kubernetes unless it pays for itself
The default is containers built once, promoted across environments, and run on whatever the simplest sufficient platform is for the project's scale — often a single VPS with a reverse proxy, sometimes a managed container service, occasionally a small fleet behind a load balancer. Boring deploys. Reproducible deploys. Deploys that one engineer can debug at 2am from a phone.
Kubernetes is brilliant when the team's scale and the project's scale both make it pay for itself. For most early-stage and mid-stage projects, it doesn't, and it consumes more senior time than the team gets back. We've seen four-person teams running fifty-node clusters because someone added it in week two. We will not do that to you.
Why defaults matter — and why they're not the same as dogma
Defaults reduce the decision cost of the boring things so we can spend our judgement on the hard ones. That's the whole game. Senior engineering is not "every choice from first principles every time, on every project, for every component". It's "first principles when the choice is load-bearing for this project, and the standard answer everywhere else".
A studio that thinks defaults are weakness ends up burning its sharpest people on bikeshed decisions. A studio that thinks defaults are non-negotiable ends up shipping ill-fitting stacks because they're convenient. The middle position — strong defaults, cheerfully overridden when the project gives a reason — is the one that actually delivers.
That's the position we hold. We won't pretend not to have it.
What you can ask us on the first call
"What stack will you use?" is a fair question, and the honest answer is short: it depends, but here is what we'd reach for first, and here is what would make us reach for something else.
Ask us. Bring the parts of your project that you think are unusual. We'll tell you which of our defaults survive contact with your constraints and which ones we'd swap, and why. That conversation is short, productive, and exactly the kind we'd have on the first call — well before anyone signs anything.
The shape of a good engagement is visible in that conversation. So is the shape of a bad one.


