Skip to content
Tech News
← Back to articles

The startup's Postgres survival guide

read original more articles
Why This Matters

This article emphasizes the importance of understanding and optimizing Postgres for production environments, highlighting practical tips for scaling and maintaining database health. It underscores that schema design and query optimization are critical for reliable, high-performance applications, especially as data complexity grows.

Key Takeaways

Alexander Belanger Co-Founder Hatchet

Over the past half year or so, I’ve been writing an internal doc for our engineers trying to distill two years of Postgres battles into a somewhat cohesive document. While I love the Postgres manual, I find it’s hard to turn to when shit hits the fan because it’s just so darn comprehensive. I thought this might be useful for others and would appreciate feedback (or other tidbits that you’ve learned running Postgres in production).

Before starting Hatchet, while I was familiar with SQL, the extent of my knowledge was basically: if a query is slow, you need an index. That’s the starting point for this doc; I’m going to assume you’re familiar with SQL basics, rows, tables, and know roughly what an index is.

And if Claude is writing all of your queries, this might be a waste of time! I recommend supabase/agent-skills

This guide should still be useful, but you might need to translate some of these tips into your ORM of choice. Lots of optimizations as you scale just aren’t possible with ORMs unless you can break past the abstraction layer and write SQL. You can do this gracefully or non-gracefully; Prisma TypedSQL or equivalents look interesting for this. We use sqlc at Hatchet which gets us very similar behavior; highly recommend if you’re a Go stack.

Let’s start with the basics: queries and schemas at low volume.

After you’re deployed, schemas are by far the hardest to change moving forward, so it’s worth spending some time on them. I’d recommend building your schema iteratively: start with a rough approximation for your tables and primary keys, then write some queries on those tables based on your application needs. You can approximate this with some questions: Is this a high-read and/or high-write table? What are the most common filters on reads? Which columns am I updating the most?

If you want to be more formal about it, you can look into database normalization into 1NF/2NF/3NF, but I’ve found normal forms to sometimes be at odds with query efficiency and ease of use, which is critical when you’re moving fast—sometimes it’s just easier to dump data into a jsonb column.

My rules of thumb for schemas are:

Use identity columns (auto-incrementing integers, slightly more performant than bigserial ) or built-in UUIDs for primary keys

... continue reading