The architecture diagram had seventeen boxes; I counted. Five were datastores: a document database for "flexibility," a search engine for "search," a vector database for "AI," a cache, and, tucked in the corner like an apology, a relational database for the actual data. The application all this served was an internal tool with twelve users. Twelve.
Each box came with its own console, billing page, failure modes, and on-call slot nobody agreed to join. Nobody budgets for the zoo because on a slide it looks like best practices. The counter-move is boring and correct: default to Postgres for business applications, adding a specialist store only when a measured constraint forces the issue.
Here's the short version. Postgres covers roughly 95% of what business applications need: relational data, semi-structured JSONB, full-text and vector search, scheduled jobs, and queues, all in one database with one backup story and one bill. A second datastore should require specific, measured evidence, and "a blog post said so" is not evidence. Boring technology is a feature; it means the edge cases got solved before you got paged.
The database zoo nobody budgeted for
Nobody sets out to build a zoo. It accretes. The document store snuck in because the client's data "doesn't fit a schema yet." The search engine arrived because someone tried LIKE queries once and they were slow. The vector database came with the AI demo, the cache came with a scary load test, and every adoption decision made sense in the meeting where it was made. The zoo is a series of locally rational choices compounding into a globally irrational system.
Count the real costs. Five datastores means five connection pools, five backup strategies (or, more likely, four backup strategies and one hope), five upgrade cycles, five sets of credentials in the secrets manager. Say each specialist store costs your small team half a day a month in care and feeding; that's two engineer-days monthly, a quarter of one engineer, spent keeping infrastructure alive instead of making the product better. For a twelve-user tool, that's not an architecture. That's a hobby.
Postgres for business applications: the default answer
Postgres has been in production for about three decades, which in software years is roughly forever. The boring problems, concurrency, transactions, constraints, replication, point-in-time recovery, were solved, argued about, and re-solved long before your project existed. Every cloud hosts it. Every ORM speaks it. Every engineer you might hire has used it, which matters more than any benchmark: the database your team already knows is worth milliseconds you'll never measure.
And here's the part the zoo-builders miss: Postgres quietly grew superpowers while everyone watched shinier demos. JSONB for schemaless data. pgvector for embeddings. Scheduled jobs, events, advisory locks. The boring default absorbed most of what the specialist stores sell, which is why it anchors the stack essentials for forward deployed work: one database, one mental model, one 3 a.m. failure surface instead of five.
JSONB: schemaless when you need it
Client data arrives messy. It shows up as a vendor's CSV with columns that change quarterly, as a JSON export from a system nobody owns anymore, as "the same fields except when it isn't." The relational answer, design the perfect schema up front, assumes a stability that client work never has. The JSONB answer is more honest: keep the fields you're sure about as real typed columns with constraints, and park the chaos in a JSONB column that accepts whatever the world throws at it.
This isn't a compromise; it's the good part of document databases without the separate database. You can index inside JSONB with a GIN index, query nested fields, and enforce structure later with constraints once the data settles down and admits what shape it actually is. Say you're integrating a field-service client's job records: the core fields become columns in week one, the weird per-customer extras live in JSONB, and by month three you've promoted the four extras that turned out to matter. Migration festival: cancelled.
pgvector: search and embeddings without a new service
The AI demo playbook says you need a vector database. So the team adds one, and now company documents live in Postgres, embeddings live somewhere else, and every search query does a distributed join across a network boundary with a vendor in the middle. It works. It's also, for most business applications, unnecessary.
pgvector stores embeddings in a column next to everything else. Semantic search over fifty thousand company documents, or five hundred thousand, runs in the same database as your users table, your permissions table, and your audit log. That last part is the one that matters: permission-aware search is a WHERE clause when the data lives together, and a distributed systems project when it doesn't. One less vendor, one less console, one less place for documents and their permissions to quietly disagree.
Cron, queues, and other boring superpowers
pg_cron runs scheduled jobs inside the database: nightly rollups, the 6 a.m. report email, the cleanup that deletes expired sessions. LISTEN/NOTIFY gives you lightweight events. Advisory locks coordinate workers. And a plain table with a status column is, no joke, a job queue good enough for most business workloads; the SELECT ... FOR UPDATE SKIP LOCKED pattern backs more production queues than any message broker vendor would like admitted.
Is it as good as a dedicated broker at millions of messages a day? No. Is your twelve-user, or twelve-hundred-user, application doing millions of messages a day? Also no. The jobs system you already have beats the jobs system you'd have to operate. When the workload is glue, nightly syncs, webhook retries, report generation, the database plus a small worker process, the same pattern that runs the Python automation layer in FDE work, covers it without a new line item.
When Postgres is the wrong answer
Zealotry in either direction produces bad architecture, so here's the honest five percent. Firehose telemetry, sensors or clickstreams writing hundreds of thousands of rows a second forever, wants a columnar or time-series store. Giant binary blobs, video and imaging, want object storage with the database holding pointers. Multi-region writes with local latency on three continents want something designed for that conflict model. And search relevance at the level of a real product's search box, typo tolerance and merchandising rules included, is a specialist tool's whole job.
Notice the pattern: every one of those is a measured constraint, not an aesthetic preference. "We might need to scale someday" is not a measured constraint; it's horoscope-driven development. Start with Postgres, instrument it, and add the specialist store the day the numbers demand it. Even at the edge, with a Cloudflare Workers front end talking to one well-run Postgres, this pattern covers more ground than the zoo ever will. One database, one backup, one bill. Spend the complexity budget on the product instead.