~/VibeHandbook
$39

Databases

postgresql.org

PostgreSQL

What it is

PostgreSQL is a mature, open-source relational that stores data in typed tables with strong support for (Structured Query Language), transactions, and data integrity. It is known for correctness, extensibility, and rich features like JSONB (Postgres's binary type), full-text search, and window functions. It is the default choice for most application backends that need reliable structured data.

Strengths

  • Think of a bank transfer that either fully completes or fully cancels — never half-done. That all-or-nothing guarantee is called ACID (Atomicity, Consistency, Isolation, Durability) transactions, and Postgres gives you strong consistency by default.
  • Powerful SQL: joins, CTEs (Common Table Expressions), window functions, and aggregates.
  • Flexible types including JSONB, arrays, ranges, and custom types.
  • Extensions like PostGIS (geospatial) and pgvector (embeddings).
  • Strong constraints, foreign keys, and check rules enforce data quality.

Trade-offs

  • Vertical scaling is easy; horizontal sharding requires extra tooling.
  • Requires schema migrations as your model evolves.
  • Heavier to operate than a single-file database for tiny projects.
  • Connection-per-client model needs a pooler (PgBouncer) at high scale.

When to use it

Reach for PostgreSQL as your default for almost any app with relational data, transactions, or reporting needs: SaaS (Software as a Service) backends, e-commerce, analytics, and anything that benefits from constraints and joins.

Vibe coding fit

When directing AI, give it the entities and their relationships, then ask it to produce a normalized schema with primary keys, foreign keys, NOT NULL constraints, and sensible indexes. Insist on parameterized queries (never string-concatenated SQL) to prevent injection, and ask the AI to wrap multi-step writes in a transaction. A good tip: tell the AI to also generate a reversible migration file. Ask it to explain each index it adds, too. That way you understand the read/write trade-offs before applying them.

CREATE TABLE users (
  id         BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email      TEXT NOT NULL UNIQUE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE orders (
  id       BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  user_id  BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  total    NUMERIC(10,2) NOT NULL CHECK (total >= 0),
  status   TEXT NOT NULL DEFAULT 'pending'
);

CREATE INDEX idx_orders_user_id ON orders(user_id);

-- Parameterized query (placeholders, not concatenation)
SELECT o.id, o.total
FROM orders o
WHERE o.user_id = $1 AND o.status = $2;