Skip to content
Insights
Analysis

Next.js, Python, and the Stack Behind a Modern Build

What each layer is for, why we choose these, and the rules we use when something new arrives.

EJU Editorial6 min readUpdated
An architecture diagram showing a browser layer, a Next.js application, Python and Node services, and a Postgres database connected by labelled paths

A modern build separates concerns cleanly. A fast front end (Next.js, React, TypeScript), a capable back end (Python or Node), reliable data (Postgres, often through Supabase), and deployment simple enough that shipping is boring.

That sentence is the whole architecture. Everything below explains why each part is there, what we deliberately leave out, and the rules we apply when something new appears, because a stack is a set of decisions rather than a list of logos.

What does a stack actually have to do?

Four jobs, in every project, whatever the technology.

Show something to a person, quickly and correctly. Enforce the rules of the business, where a user cannot tamper with them. Store the truth, so the same question always returns the same answer. And run reliably, with a way to release changes without fear.

Most bad architecture comes from mixing those four together. Business rules in the browser. Truth living in three places. Releases that require a person to remember six steps.

The front end: Next.js, React, TypeScript

Next.js renders on the server, which matters for two reasons a business owner cares about: pages arrive fast, and search engines and AI assistants can read them properly. A single page application that renders only in the browser starts at a disadvantage on both counts.

The framework has settled considerably. The App Router is the default pattern for new projects, Turbopack is now the default bundler, and the current line sits at Next.js 16, with the 15.5 line kept as maintenance LTS for teams who are not ready to move. React 19.2 features arrive through it. For an existing production application, gradual migration is almost always better than a rewrite.

TypeScript is not optional in our work. It catches the entire class of errors where a field is missing, renamed, or arrives in the wrong shape, which is the most common cause of late night breakage in JavaScript projects. It costs a little discipline and saves a great deal of debugging.

The back end: Python and Node, chosen per job

We use both, and the choice is practical rather than tribal.

Node when the work is close to the front end and benefits from sharing types and code across the boundary. Straightforward APIs, session handling, server actions, anything where one team owning one language keeps things simple.

Python when the work is data or intelligence. Document parsing, extraction, scheduled processing, integrations with analytical libraries, and anything touching AI models. The ecosystem is deeper for this kind of work and the code is usually shorter and easier to read.

They coexist through a plain boundary: an HTTP service or a queue. The Next.js application does not import Python code. It asks a service for a result and handles the case where the answer does not come back. That boundary is what keeps a mixed stack from becoming a mess.

The data layer: Postgres, usually through Supabase

Postgres is the default, and it takes a strong argument to move us off it. It handles relational data, JSON when needed, full text search, and constraints that stop bad records existing in the first place. Constraints matter more than people expect, because the database is the last line of defense when application code has a bug.

Supabase gives us Postgres with authentication, storage, and access rules attached, which removes weeks of undifferentiated work at the start of a project. We wrote about it honestly in a separate review, including the parts that bite in production. The important property is that it is real Postgres underneath, so there is always a route out.

Row level security deserves a mention on its own. Putting access rules next to the data, rather than scattered through application code, means a user physically cannot read another tenant's records even when a screen has a bug.

The parts nobody puts on a stack diagram

Background jobs and queues. Anything slow, external, or retryable belongs in a job rather than in a web request. Sending messages, generating documents, calling third party systems, processing uploads. Web requests should return in milliseconds.

Idempotency. Payment webhooks and message deliveries arrive twice more often than people expect. Every handler that changes state needs to be safe to run more than once.

Migrations and environments. Schema changes tracked in version control, applied the same way everywhere, with a staging environment that mirrors production.

Observability. Error tracking and logs from day one. Not because it is exciting, but because the alternative is learning about failures from customers.

Backups you have actually restored. An untested backup is a belief, not a plan.

How we decide when something new comes along

Boring by default. New tools need to be meaningfully better than the boring option, not merely newer. The cost of an unusual choice is paid every month, by whoever maintains it.

One database until proven otherwise. Most performance problems are a missing index, not a missing technology.

Add a service only when it has a genuinely different profile. A different runtime, a different scaling shape, or a different failure domain. Splitting an application into services because it feels tidy buys a distributed systems problem that a small team cannot afford.

Prefer managed, until the bill or the constraints say otherwise. Paying someone to run Postgres is cheaper than paying an engineer to page at three in the morning.

Always keep an exit. Open formats, standard SQL, portable containers. Any tool we cannot leave is a tool we do not adopt.

What we do not use by default

Microservices for teams of under ten. NoSQL as the primary store when the data is obviously relational. Hand written authentication, when solved implementations exist and the failure mode is a breach. And the newest framework in a client's production system, because someone has to maintain it for five years.

None of these are wrong in the right context. They are just wrong as defaults.

Why this matters if you are not technical

Three reasons, and they are commercial rather than technical.

Speed of change. A clean separation means a change to how something looks does not risk how it calculates. That is the difference between a change taking a day and taking a fortnight.

Cost of ownership. Ordinary, widely used technology means you can hire, replace, or get a second opinion at any point. Exotic choices quietly make you dependent on whoever built it.

Survivability. Standard SQL, portable code, and documented deployment mean the thing you paid for outlives the relationship with whoever built it. That should be true of any software you own.

The quieter point

A stack is not a badge. It is a set of trade offs that either make change safe and cheap over the next five years, or make it slow and frightening.

This is the stack we build on, chosen because it is fast to work in, ordinary enough to hand over, and honest about where the complexity actually lives.

Share
Link copied

Get the next one by email

Reporting, reviews, and guides on AI and automation, sent as they publish. No sequences.

We use your email only to send this newsletter. See our privacy policy.