Skip to main content
Backend

Why We Default to Node.js LTS and PostgreSQL for Backend

We argue that Node.js LTS and PostgreSQL form the most reliable backend stack, using LTS support windows, ACID transactions, and indexing to make our case.

We build backends for a living, so we care about one number above all: 30 months. That's how long critical bugs get fixed in a Node.js LTS release, according to the Node.js release schedule (Node.js official documentation (releases)). If you're still running a Current release in production, you're borrowing against your own stability. The fix is boring and correct: pin to Active LTS, and pair it with a relational database that treats your data like it matters. Our thesis: for the vast majority of full-stack projects, the backend should be Node.js LTS plus PostgreSQL. Not because it's trendy, but because it minimizes the class of failures we actually get paged for.

The LTS window is the only support contract you get

Major Node.js versions sit in Current for six months, then even-numbered releases historically move to Active LTS. Production apps should only use Active LTS or Maintenance LTS (Node.js official documentation (releases)). That's not a suggestion; it's the difference between patching a CVE on a Tuesday afternoon and explaining to your CTO why the runtime is end-of-life. We've seen teams chase the newest features and then scramble when a security fix lands only on LTS. Pick the boring version. Your future self will thank you.

PostgreSQL's transaction model is a feature, not overhead

A transaction bundles multiple steps into a single all-or-nothing operation: if a failure occurs partway through, none of the steps affect the database (PostgreSQL official documentation). In PostgreSQL, a transaction is atomic, and you delimit it with BEGIN and COMMIT, with ROLLBACK cancelling all updates (PostgreSQL official documentation). That matters when you're moving money, updating inventory, or writing an order and its line items together. MongoDB now supports multi-document ACID transactions too, but you have to opt in and think about it. With Postgres, atomicity is the default posture. We'd rather have the safe default.

Indexes: the difference between a scan and a walk

Without an index, PostgreSQL scans an entire table row by row to find matching entries; with an index on the relevant column, it can walk a search tree a few levels deep (PostgreSQL official documentation (indexes)). We've watched a 200ms endpoint turn into 2ms after adding one index. The catch: every index slows writes, because each insert must update it. So we index what we query, not what we might query. That discipline is the whole game.

The counter-argument: document databases are faster to start

The strongest case against our default is that MongoDB lets you model data the way application code uses it, storing flexible JSON-like documents (MongoDB official documentation). For a prototype or a product with genuinely fluid schema, that's real. We reject it as a default for two reasons. First, schema flexibility is a loan against future migrations; once you have 50 million documents with three different shapes, you'll wish you'd had a migration story from day one. Second, MongoDB's own docs note that without indexes it must scan every document in a collection (MongoDB official documentation (indexes)). The same indexing discipline applies. So the speed advantage is mostly at the start, and the cost shows up later. We'll take Postgres and a migration tool.

Criterion Node.js + PostgreSQL Node.js + MongoDB
Backend language/framework Node.js with Express Node.js with Express
Data model Tables, rows, SQL Flexible JSON-like documents
Transactions Atomic by default, BEGIN/COMMIT Multi-document ACID, opt-in
Indexing B-tree, avoids full table scan B-tree, avoids full collection scan
Best for Relational integrity, complex queries Rapid prototyping, fluid schema

We still use MongoDB, just not as the default

There's a time and place. If we're building a content feed where documents vary wildly and we need to iterate on shape weekly, MongoDB's aggregation pipeline is genuinely nice: stages process documents, each stage filters or groups, and output flows to the next stage (MongoDB official documentation (aggregation pipeline)). But for anything with money, inventory, or user permissions, we want the relational model and the atomic guarantees. That's not dogma; it's pattern recognition from incidents.

  • Pin to Active LTS or Maintenance LTS; never ship Current to production (Node.js official documentation (releases)).
  • Wrap multi-step writes in BEGIN/COMMIT; test the ROLLBACK path (PostgreSQL official documentation).
  • Add indexes for your real queries, then watch write latency (PostgreSQL official documentation (indexes)).

Quick tip: If you're on a Current release right now, schedule the LTS upgrade this sprint. The six-month Current window is shorter than most planning cycles.

What I'd actually do

Start every new backend on Active LTS Node.js with Express, and PostgreSQL as the primary datastore. Use transactions for any write that touches more than one row. Add indexes as you add queries, and measure write impact. If you hit a genuine document-shaped problem, add MongoDB alongside Postgres for that specific workload, not as a replacement. This stack won't win architecture Twitter, but it will keep your pager quiet. And that's the only metric that matters at 3 a.m.

Sources

  • Node.js official documentation (releases) - https://nodejs.org/en/about/previous-releases
  • PostgreSQL official documentation - https://www.postgresql.org/docs/current/tutorial-transactions.html
  • PostgreSQL official documentation (indexes) - https://www.postgresql.org/docs/current/indexes-intro.html
  • MongoDB official documentation - https://www.mongodb.com/docs/manual/
  • MongoDB official documentation (indexes) - https://www.mongodb.com/docs/manual/indexes/
  • MongoDB official documentation (aggregation pipeline) - https://www.mongodb.com/docs/manual/core/aggregation-pipeline/

Share this article:

Comments (0)

No comments yet. Be the first to comment!