π DECEMBER RETROSPECTIVE
The month is almost over.
Let's count what happened.
---
BY THE NUMBERS
Subscribers Dec 1: ~1,000
Subscribers now: 1364
---
WHAT WORKED THIS MONTH
β Daily accountability posts
β Simple, actionable content
β Community sharing progress
β "Ship broken, not perfect" mindset
β PostgreSQL doing everything
---
WHAT I LEARNED RUNNING THIS
β You want simple, not complex
β You want "today" not "someday"
β You're building real things, not just learning
β Community momentum is real
---
THE REAL WIN
December wasn't about finishing.
It was about starting.
If you wrote one line of code,
created one table,
deployed one thing β
You're ahead of December 1st you.
---
WHAT'S NEXT
January: New challenge?
Keep shipping?
Something different?
What do you want from @postgres in 2025?
Reply below. Seriously.
This shapes what comes next.
---
Thank you for being here.
1,000+ people learning PostgreSQL together.
That's pretty cool.
See you next year. π
@postgres
#DecemberShipping
The month is almost over.
Let's count what happened.
---
BY THE NUMBERS
Subscribers Dec 1: ~1,000
Subscribers now: 1364
---
WHAT WORKED THIS MONTH
β Daily accountability posts
β Simple, actionable content
β Community sharing progress
β "Ship broken, not perfect" mindset
β PostgreSQL doing everything
---
WHAT I LEARNED RUNNING THIS
β You want simple, not complex
β You want "today" not "someday"
β You're building real things, not just learning
β Community momentum is real
---
THE REAL WIN
December wasn't about finishing.
It was about starting.
If you wrote one line of code,
created one table,
deployed one thing β
You're ahead of December 1st you.
---
WHAT'S NEXT
January: New challenge?
Keep shipping?
Something different?
What do you want from @postgres in 2025?
Reply below. Seriously.
This shapes what comes next.
---
Thank you for being here.
1,000+ people learning PostgreSQL together.
That's pretty cool.
See you next year. π
@postgres
#DecemberShipping
π― 2026 STARTS NOW
December is done. Holidays over.
No more excuses.
What are you building this year?
---
DECEMBER RECAP
We had 200+ people join the shipping challenge.
Dozens of projects launched.
Some ugly. Some broken. All real.
That momentum doesn't have to stop.
---
2026: SIMPLER APPROACH
Last year I overcomplicated things.
Big masterclasses. Complex systems.
This year: small wins.
One useful thing per week.
Something you can implement in an hour.
Actually use it. Move on.
---
THIS WEEK
Mon: Fresh start (you're here)
Tue: The 3-table SaaS (all you actually need)
Wed: π° Copy-paste auth queries (3 β)
Thu: Your first mass email (from PostgreSQL)
Fri: Week 1 check-in
Simple. Useful. Cheap.
---
QUESTION FOR YOU
What's the ONE thing you want to build in January?
Not a roadmap. Not a vision.
One thing. Reply below.
Let's make it happen.
#January2026
December is done. Holidays over.
No more excuses.
What are you building this year?
---
DECEMBER RECAP
We had 200+ people join the shipping challenge.
Dozens of projects launched.
Some ugly. Some broken. All real.
That momentum doesn't have to stop.
---
2026: SIMPLER APPROACH
Last year I overcomplicated things.
Big masterclasses. Complex systems.
This year: small wins.
One useful thing per week.
Something you can implement in an hour.
Actually use it. Move on.
---
THIS WEEK
Mon: Fresh start (you're here)
Tue: The 3-table SaaS (all you actually need)
Wed: π° Copy-paste auth queries (3 β)
Thu: Your first mass email (from PostgreSQL)
Fri: Week 1 check-in
Simple. Useful. Cheap.
---
QUESTION FOR YOU
What's the ONE thing you want to build in January?
Not a roadmap. Not a vision.
One thing. Reply below.
Let's make it happen.
#January2026
β€2
ποΈ THE 3-TABLE SAAS
Every SaaS tutorial shows you 47 tables.
Users, profiles, settings, preferences,
organizations, memberships, roles, permissions...
You don't need that.
You need 3 tables to start making money.
---
TABLE 1: USERS
That's it. No name, no profile, no avatar.
Add them when ONE user asks for them.
---
TABLE 2: THE THING YOU'RE SELLING
Projects, documents, dashboards, reports β
whatever your product creates.
---
TABLE 3: PAYMENTS
Not subscriptions. Just payments.
Stripe Checkout handles the rest.
---
THAT'S THE WHOLE DATABASE
3 tables. 20 lines of SQL.
Add complexity when you need it.
Not before.
---
Tomorrow: Auth queries that work with this.
Session management.
Password reset.
Magic links.
All copy-paste ready.
3 β β less than a coffee.
#January2026
Every SaaS tutorial shows you 47 tables.
Users, profiles, settings, preferences,
organizations, memberships, roles, permissions...
You don't need that.
You need 3 tables to start making money.
---
TABLE 1: USERS
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
That's it. No name, no profile, no avatar.
Add them when ONE user asks for them.
---
TABLE 2: THE THING YOU'RE SELLING
CREATE TABLE projects ( -- or whatever your thing is
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
name TEXT NOT NULL,
data JSONB DEFAULT '{}', -- flexibility for now
created_at TIMESTAMPTZ DEFAULT NOW()
);
Projects, documents, dashboards, reports β
whatever your product creates.
---
TABLE 3: PAYMENTS
CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
stripe_payment_id TEXT,
amount INTEGER, -- cents
status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT NOW()
);
Not subscriptions. Just payments.
Stripe Checkout handles the rest.
---
THAT'S THE WHOLE DATABASE
3 tables. 20 lines of SQL.
Add complexity when you need it.
Not before.
---
Tomorrow: Auth queries that work with this.
Session management.
Password reset.
Magic links.
All copy-paste ready.
3 β β less than a coffee.
#January2026
This media is not supported in the widget
VIEW IN TELEGRAM
π§ YOUR FIRST MASS EMAIL
You have a waitlist. Or users. Or both.
Time to email them.
No Mailchimp. No Sendgrid dashboard.
Just PostgreSQL + one API call.
---
THE SIMPLE WAY
Export. Paste into your email tool.
Or loop through in code.
---
TRACK WHAT YOU SEND
No double-sends. Ever.
---
SIMPLE UNSUBSCRIBE
---
THE ACTUAL SENDING
Use what you have:
- Resend: 3,000 free/month
- Postmark: 100 free/month
- AWS SES: $0.10 per 1,000
One API call per email.
PostgreSQL handles the logic.
---
YOUR ASSIGNMENT
If you have ANY users or waitlist signups,
send them an email this week.
"Hey, just checking in. Still interested in [thing]?"
That's it. You'll learn something.
---
Tomorrow: Week 1 check-in.
What did you ship?
#January2026
You have a waitlist. Or users. Or both.
Time to email them.
No Mailchimp. No Sendgrid dashboard.
Just PostgreSQL + one API call.
---
THE SIMPLE WAY
-- Get everyone who should receive this email
SELECT email, name
FROM users
WHERE created_at > '2025-12-01'
AND email_verified = true
AND unsubscribed = false;
Export. Paste into your email tool.
Or loop through in code.
---
TRACK WHAT YOU SEND
CREATE TABLE email_log (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id),
email_type TEXT, -- welcome, update, promo
sent_at TIMESTAMPTZ DEFAULT NOW()
);
-- Before sending, check:
SELECT user_id FROM email_log
WHERE email_type = 'jan_update'
AND user_id = $1;
-- If empty, safe to send
No double-sends. Ever.
---
SIMPLE UNSUBSCRIBE
ALTER TABLE users ADD COLUMN unsubscribed BOOLEAN DEFAULT false;
-- Unsubscribe link: /unsubscribe?token=xxx
UPDATE users SET unsubscribed = true
WHERE id = (SELECT user_id FROM unsubscribe_tokens WHERE token = $1);
---
THE ACTUAL SENDING
Use what you have:
- Resend: 3,000 free/month
- Postmark: 100 free/month
- AWS SES: $0.10 per 1,000
One API call per email.
PostgreSQL handles the logic.
---
YOUR ASSIGNMENT
If you have ANY users or waitlist signups,
send them an email this week.
"Hey, just checking in. Still interested in [thing]?"
That's it. You'll learn something.
---
Tomorrow: Week 1 check-in.
What did you ship?
#January2026
β€1
π WEEK 1 CHECK-IN
First week of 2026. Done.
---
THIS WEEK WE COVERED
Mon: Fresh start, what are you building
Tue: 3-table SaaS (users, things, payments)
Wed: Copy-paste auth queries π°
Thu: Mass email from PostgreSQL
Simple stuff. Foundational stuff.
---
WHAT DID YOU DO?
Reply with one of these:
- π Started something new
- π§ Worked on existing project
- π Just learning for now
- π΄ Honestly, not much
No judgment. Just curious where everyone is.
---
NEXT WEEK PREVIEW
More simple wins:
- Background jobs without Redis
- The $0 monitoring stack
- Making PostgreSQL fast (indexes that matter)
Still keeping it simple.
Still keeping it cheap.
---
QUICK POLL
The 3 β pricing β better or worse?
Too cheap = feels low quality?
Just right = removes friction?
Genuinely want to know.
---
See you Monday.
First week done. 51 to go.
#January2026
First week of 2026. Done.
---
THIS WEEK WE COVERED
Mon: Fresh start, what are you building
Tue: 3-table SaaS (users, things, payments)
Wed: Copy-paste auth queries π°
Thu: Mass email from PostgreSQL
Simple stuff. Foundational stuff.
---
WHAT DID YOU DO?
Reply with one of these:
- π Started something new
- π§ Worked on existing project
- π Just learning for now
- π΄ Honestly, not much
No judgment. Just curious where everyone is.
---
NEXT WEEK PREVIEW
More simple wins:
- Background jobs without Redis
- The $0 monitoring stack
- Making PostgreSQL fast (indexes that matter)
Still keeping it simple.
Still keeping it cheap.
---
QUICK POLL
The 3 β pricing β better or worse?
Too cheap = feels low quality?
Just right = removes friction?
Genuinely want to know.
---
See you Monday.
First week done. 51 to go.
#January2026
PostgreSQL Pro | Database Mastery pinned Β«π COPY-PASTE AUTH QUERIES Stop rewriting authentication from scratch. --- WHAT'S INSIDE π User Signup - Email validation - Password hashing (pgcrypto) - Duplicate prevention π Login Check - Password verification - Return user or null - One query π Sessionβ¦Β»
β‘ WEEK 2: NO MORE REDIS
Last week: Auth queries.
This week: Background jobs.
---
THE PROBLEM
Your app needs to:
- Send emails without blocking requests
- Process uploads after response
- Run daily reports
- Clean up old data
- Sync with external APIs
The "normal" solution:
Redis + Sidekiq/Bull/Celery
The cost:
- Another service to run ($15-50/month)
- Another thing to break
- Another thing to monitor
---
THE POSTGRESQL WAY
Your database can be your job queue.
Not "for small projects."
Not "until you scale."
For real production apps.
---
THIS WEEK
Tue: How PostgreSQL job queues work
Wed: π° Copy-paste job queue (4 β)
Thu: Scheduling with pg_cron
Fri: Week 2 check-in
---
LAST WEEK'S WIN
The auth queries got their first buyer. π
Small wins. Real progress.
That's the 2026 energy.
---
What background tasks do you need to run?
#January2026
Last week: Auth queries.
This week: Background jobs.
---
THE PROBLEM
Your app needs to:
- Send emails without blocking requests
- Process uploads after response
- Run daily reports
- Clean up old data
- Sync with external APIs
The "normal" solution:
Redis + Sidekiq/Bull/Celery
The cost:
- Another service to run ($15-50/month)
- Another thing to break
- Another thing to monitor
---
THE POSTGRESQL WAY
Your database can be your job queue.
Not "for small projects."
Not "until you scale."
For real production apps.
---
THIS WEEK
Tue: How PostgreSQL job queues work
Wed: π° Copy-paste job queue (4 β)
Thu: Scheduling with pg_cron
Fri: Week 2 check-in
---
LAST WEEK'S WIN
The auth queries got their first buyer. π
Small wins. Real progress.
That's the 2026 energy.
---
What background tasks do you need to run?
#January2026
π POSTGRESQL AS A JOB QUEUE
"Use Redis for queues."
Why? PostgreSQL handles queues fine.
Here's how it works.
---
THE BASIC IDEA
Add job:
Get next job:
---
THE MAGIC: FOR UPDATE SKIP LOCKED
Multiple workers can poll the same table.
Each gets a different job.
No duplicates. No conflicts.
PostgreSQL handles the locking.
---
WHAT YOU NEED FOR PRODUCTION
Basic table? Easy.
Production-ready? Needs more:
- Retry logic for failures
- Dead letter queue for permanent failures
- Priority levels
- Scheduled jobs (run at specific time)
- Job timeout handling
- Proper indexes
---
Tomorrow: All of that. Copy-paste ready.
4 β β Replace your Redis bill forever.
#January2026
"Use Redis for queues."
Why? PostgreSQL handles queues fine.
Here's how it works.
---
THE BASIC IDEA
CREATE TABLE jobs (
id SERIAL PRIMARY KEY,
task TEXT NOT NULL,
payload JSONB,
status TEXT DEFAULT 'pending',
run_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW()
);
Add job:
INSERT INTO jobs (task, payload)
VALUES ('send_email', '{"to": "[email protected]"}');
Get next job:
UPDATE jobs SET status = 'processing'
WHERE id = (
SELECT id FROM jobs
WHERE status = 'pending' AND run_at <= NOW()
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED -- This is the magic
)
RETURNING *;
---
THE MAGIC: FOR UPDATE SKIP LOCKED
Multiple workers can poll the same table.
Each gets a different job.
No duplicates. No conflicts.
PostgreSQL handles the locking.
---
WHAT YOU NEED FOR PRODUCTION
Basic table? Easy.
Production-ready? Needs more:
- Retry logic for failures
- Dead letter queue for permanent failures
- Priority levels
- Scheduled jobs (run at specific time)
- Job timeout handling
- Proper indexes
---
Tomorrow: All of that. Copy-paste ready.
4 β β Replace your Redis bill forever.
#January2026
β€1
This media is not supported in the widget
VIEW IN TELEGRAM
PostgreSQL Pro | Database Mastery pinned Β«β‘ COPY-PASTE JOB QUEUE Background jobs without Redis. Production-ready. 15 minutes to implement. --- WHAT'S INSIDE π Complete Schema - Jobs table with all fields you need - Proper indexes for fast polling - Status tracking (pending β processing β done/failed)β¦Β»
β° SCHEDULING WITH PG_CRON
Background jobs: done.
But what about scheduled tasks?
"Run this every night at 3 AM"
"Send weekly report every Monday"
"Clean up old data daily"
---
PG_CRON: CRON INSIDE POSTGRESQL
That's it. Runs inside PostgreSQL.
---
COMMON SCHEDULES
---
MANAGE YOUR SCHEDULES
---
THE COMBO
pg_cron schedules β inserts into jobs table β workers process
No external scheduler.
No cron on server.
All in PostgreSQL.
---
WHERE TO GET PG_CRON
β Supabase: Built-in
β Neon: Available
β Railway: Available
β Render: Available
β Self-hosted:
Most managed PostgreSQL has it now.
---
What do you need to schedule?
#January2026
Background jobs: done.
But what about scheduled tasks?
"Run this every night at 3 AM"
"Send weekly report every Monday"
"Clean up old data daily"
---
PG_CRON: CRON INSIDE POSTGRESQL
-- Enable the extension
CREATE EXTENSION pg_cron;
-- Run every night at 3 AM
SELECT cron.schedule(
'nightly-cleanup',
'0 3 * * *',
$$DELETE FROM sessions WHERE expires_at < NOW()$$
);
-- Run every Monday at 9 AM
SELECT cron.schedule(
'weekly-report',
'0 9 * * 1',
$$INSERT INTO jobs (task, payload) VALUES ('send_weekly_report', '{}')$$
);
That's it. Runs inside PostgreSQL.
---
COMMON SCHEDULES
-- Every minute
'* * * * *'
-- Every hour
'0 * * * *'
-- Every day at midnight
'0 0 * * *'
-- Every Monday at 9 AM
'0 9 * * 1'
-- First day of month
'0 0 1 * *'
---
MANAGE YOUR SCHEDULES
-- List all jobs
SELECT * FROM cron.job;
-- Disable a job
SELECT cron.unschedule('nightly-cleanup');
-- See job history
SELECT * FROM cron.job_run_details
ORDER BY start_time DESC LIMIT 10;
---
THE COMBO
pg_cron schedules β inserts into jobs table β workers process
No external scheduler.
No cron on server.
All in PostgreSQL.
---
WHERE TO GET PG_CRON
β Supabase: Built-in
β Neon: Available
β Railway: Available
β Render: Available
β Self-hosted:
apt install postgresql-14-cronMost managed PostgreSQL has it now.
---
What do you need to schedule?
#January2026
β€1
π WEEK 2 CHECK-IN
Background jobs week: done.
---
THIS WEEK
Mon: Why PostgreSQL for job queues
Tue: How FOR UPDATE SKIP LOCKED works
Wed: Complete job queue system π°
Thu: pg_cron for scheduling
---
THE REDIS QUESTION
"But Redis is faster for queues!"
Sure. Redis is faster.
But is your bottleneck really queue speed?
For 99% of apps:
- PostgreSQL queue: Fast enough
- One less service: Priceless
- Simpler stack: Fewer bugs
Optimize when you need to.
Not before.
---
WHAT DID YOU IMPLEMENT?
- β‘ Set up a job queue
- β° Added pg_cron schedules
- π Just learning for now
- π€ Still thinking about it
---
NEXT WEEK PREVIEW
Making PostgreSQL fast.
Not "optimization theory."
Actual indexes. Actual queries.
Before/after benchmarks.
The stuff that makes your app stop being slow.
---
MONTH CHECK-IN
Week 1: Auth β
Week 2: Background jobs β
Week 3: Performance (coming)
Week 4: ???
What should Week 4 be?
Reply with requests.
---
See you Monday.
#January2026
Background jobs week: done.
---
THIS WEEK
Mon: Why PostgreSQL for job queues
Tue: How FOR UPDATE SKIP LOCKED works
Wed: Complete job queue system π°
Thu: pg_cron for scheduling
---
THE REDIS QUESTION
"But Redis is faster for queues!"
Sure. Redis is faster.
But is your bottleneck really queue speed?
For 99% of apps:
- PostgreSQL queue: Fast enough
- One less service: Priceless
- Simpler stack: Fewer bugs
Optimize when you need to.
Not before.
---
WHAT DID YOU IMPLEMENT?
- β‘ Set up a job queue
- β° Added pg_cron schedules
- π Just learning for now
- π€ Still thinking about it
---
NEXT WEEK PREVIEW
Making PostgreSQL fast.
Not "optimization theory."
Actual indexes. Actual queries.
Before/after benchmarks.
The stuff that makes your app stop being slow.
---
MONTH CHECK-IN
Week 1: Auth β
Week 2: Background jobs β
Week 3: Performance (coming)
Week 4: ???
What should Week 4 be?
Reply with requests.
---
See you Monday.
#January2026
β€1
π’ WHY YOUR QUERIES ARE SLOW
"PostgreSQL is slow."
No. Your queries are slow.
PostgreSQL is waiting for you to help it.
---
THE USUAL SUSPECTS
1. Missing indexes
PostgreSQL scans every row.
Millions of rows = slow.
2. Wrong indexes
You have indexes.
They're not being used.
3. Too much data
SELECT * when you need 2 columns.
JOINs that pull entire tables.
4. N+1 queries
1 query to get users.
100 queries to get their posts.
Death by a thousand cuts.
---
THE GOOD NEWS
Most slow queries are fixed with:
- One or two indexes
- Minor query rewrites
- EXPLAIN ANALYZE (so you know what's happening)
No magic. No expensive consultants.
Just knowing where to look.
---
THIS WEEK
Tue: Indexes that actually matter
Wed: π° Query speedup cheat sheet (3 β)
Thu: EXPLAIN ANALYZE decoded
Fri: Week 3 check-in
---
What's your slowest query right now?
Paste it below. Let's fix it together.
#January2026
"PostgreSQL is slow."
No. Your queries are slow.
PostgreSQL is waiting for you to help it.
---
THE USUAL SUSPECTS
1. Missing indexes
PostgreSQL scans every row.
Millions of rows = slow.
2. Wrong indexes
You have indexes.
They're not being used.
3. Too much data
SELECT * when you need 2 columns.
JOINs that pull entire tables.
4. N+1 queries
1 query to get users.
100 queries to get their posts.
Death by a thousand cuts.
---
THE GOOD NEWS
Most slow queries are fixed with:
- One or two indexes
- Minor query rewrites
- EXPLAIN ANALYZE (so you know what's happening)
No magic. No expensive consultants.
Just knowing where to look.
---
THIS WEEK
Tue: Indexes that actually matter
Wed: π° Query speedup cheat sheet (3 β)
Thu: EXPLAIN ANALYZE decoded
Fri: Week 3 check-in
---
What's your slowest query right now?
Paste it below. Let's fix it together.
#January2026
π INDEXES THAT ACTUALLY MATTER
"Just add an index."
Which one? Where? On what?
Here's what actually matters.
---
RULE 1: INDEX YOUR WHERE CLAUSES
No index = scan every row.
With index = jump straight to it.
---
RULE 2: INDEX YOUR JOIN COLUMNS
---
RULE 3: INDEX YOUR ORDER BY
One index. Filters AND sorts.
---
RULE 4: PARTIAL INDEXES FOR FILTERED QUERIES
---
THE MISTAKE EVERYONE MAKES
Adding indexes on everything.
More indexes = slower writes.
Every INSERT/UPDATE updates every index.
Index what you query.
Nothing more.
---
Tomorrow: The full cheat sheet.
When to use B-tree vs GIN vs GiST.
Composite index column order.
Indexes that hurt more than help.
3 β β Know exactly which index to add.
#January2026
"Just add an index."
Which one? Where? On what?
Here's what actually matters.
---
RULE 1: INDEX YOUR WHERE CLAUSES
-- This query:
SELECT * FROM users WHERE email = '[email protected]';
-- Needs this index:
CREATE INDEX idx_users_email ON users(email);
No index = scan every row.
With index = jump straight to it.
---
RULE 2: INDEX YOUR JOIN COLUMNS
-- This query:
SELECT * FROM posts
JOIN users ON posts.user_id = users.id;
-- Needs:
CREATE INDEX idx_posts_user_id ON posts(user_id);
-- (users.id is already indexed as PRIMARY KEY)
---
RULE 3: INDEX YOUR ORDER BY
-- This query:
SELECT * FROM posts
WHERE user_id = $1
ORDER BY created_at DESC;
-- Needs a composite index:
CREATE INDEX idx_posts_user_created
ON posts(user_id, created_at DESC);
One index. Filters AND sorts.
---
RULE 4: PARTIAL INDEXES FOR FILTERED QUERIES
-- If you always query active users:
SELECT * FROM users WHERE status = 'active';
-- Don't index everything:
CREATE INDEX idx_users_active
ON users(id)
WHERE status = 'active';
-- Smaller index. Faster lookups.
---
THE MISTAKE EVERYONE MAKES
Adding indexes on everything.
More indexes = slower writes.
Every INSERT/UPDATE updates every index.
Index what you query.
Nothing more.
---
Tomorrow: The full cheat sheet.
When to use B-tree vs GIN vs GiST.
Composite index column order.
Indexes that hurt more than help.
3 β β Know exactly which index to add.
#January2026
β€2
This media is not supported in the widget
VIEW IN TELEGRAM
π€‘1
PostgreSQL Pro | Database Mastery pinned Β«β‘ QUERY SPEEDUP CHEAT SHEET Stop guessing. Know exactly what to do. --- WHAT'S INSIDE π Index Decision Tree "I have this query β I need this index" No theory. Just answers. π Index Type Guide - B-tree: When and why (90% of cases) - GIN: For JSONB andβ¦Β»
π EXPLAIN ANALYZE DECODED
The most useful command nobody understands.
Let's decode what it tells you.
---
THE OUTPUT (SCARY VERSION)
Seq Scan on users (cost=0.00..1234.00 rows=1 width=65) (actual time=45.123..45.456 rows=1 loops=1)
Filter: (email = '[email protected]'::text)
Rows Removed by Filter: 50000
Planning Time: 0.123 ms
Execution Time: 45.567 ms
---
THE OUTPUT (TRANSLATED)
Seq Scan on users
β "I scanned every row in the table"
β π¨ RED FLAG for large tables
cost=0.00..1234.00
β PostgreSQL's guess at effort
β Higher = more work
actual time=45.123..45.456
β Real time in milliseconds
β This is what matters
rows=1
β Found 1 matching row
Rows Removed by Filter: 50000
β "I looked at 50,000 rows to find 1"
β π¨ You need an index
---
AFTER ADDING INDEX
Index Scan using idx_users_email on users (cost=0.42..8.44 rows=1 width=65) (actual time=0.045..0.047 rows=1 loops=1)
Index Cond: (email = '[email protected]'::text)
Planning Time: 0.156 ms
Execution Time: 0.078 ms
45ms β 0.078ms = 577x faster
---
WHAT TO LOOK FOR
β Good signs:
-
- Low
-
π¨ Bad signs:
-
-
-
-
---
YOUR TURN
Run EXPLAIN ANALYZE on your slowest query.
What do you see?
#January2026
The most useful command nobody understands.
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
Let's decode what it tells you.
---
THE OUTPUT (SCARY VERSION)
Seq Scan on users (cost=0.00..1234.00 rows=1 width=65) (actual time=45.123..45.456 rows=1 loops=1)
Filter: (email = '[email protected]'::text)
Rows Removed by Filter: 50000
Planning Time: 0.123 ms
Execution Time: 45.567 ms
---
THE OUTPUT (TRANSLATED)
Seq Scan on users
β "I scanned every row in the table"
β π¨ RED FLAG for large tables
cost=0.00..1234.00
β PostgreSQL's guess at effort
β Higher = more work
actual time=45.123..45.456
β Real time in milliseconds
β This is what matters
rows=1
β Found 1 matching row
Rows Removed by Filter: 50000
β "I looked at 50,000 rows to find 1"
β π¨ You need an index
---
AFTER ADDING INDEX
CREATE INDEX idx_users_email ON users(email);
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
Index Scan using idx_users_email on users (cost=0.42..8.44 rows=1 width=65) (actual time=0.045..0.047 rows=1 loops=1)
Index Cond: (email = '[email protected]'::text)
Planning Time: 0.156 ms
Execution Time: 0.078 ms
45ms β 0.078ms = 577x faster
---
WHAT TO LOOK FOR
β Good signs:
-
Index Scan or Index Only Scan- Low
actual time-
rows close to estimatedπ¨ Bad signs:
-
Seq Scan on big tables-
Rows Removed by Filter is huge-
actual time in seconds-
loops=10000 (N+1 problem)---
YOUR TURN
Run EXPLAIN ANALYZE on your slowest query.
What do you see?
#January2026
β€1π1
π WEEK 3 CHECK-IN
Performance week: done.
---
THIS WEEK
Mon: Why queries are slow (the usual suspects)
Tue: Indexes that matter (rules 1-4)
Wed: Query speedup cheat sheet π°
Thu: EXPLAIN ANALYZE decoded
---
THE SIMPLE TRUTH
90% of slow PostgreSQL:
β Missing index
β Fix: CREATE INDEX
9% of slow PostgreSQL:
β Wrong query pattern
β Fix: Rewrite query
1% of slow PostgreSQL:
β Actually needs tuning
β Fix: postgresql.conf
Start with indexes. Always.
---
DID YOU TRY IT?
- β‘ Added an index, saw improvement
- π Ran EXPLAIN ANALYZE, learned something
- π Just reading for now
- π€· My queries are fine (lucky you)
---
NEXT WEEK
Week 4. What do you want?
Options:
A) Full-text search (replace Algolia)
B) JSONB patterns (flexible schemas)
C) Database backups (don't lose everything)
D) Something else?
Reply with A, B, C, or your idea.
Most votes wins.
---
JANUARY PROGRESS
Week 1: Auth β
Week 2: Background jobs β
Week 3: Performance β
Week 4: You decide
---
See you Monday.
#January2026
Performance week: done.
---
THIS WEEK
Mon: Why queries are slow (the usual suspects)
Tue: Indexes that matter (rules 1-4)
Wed: Query speedup cheat sheet π°
Thu: EXPLAIN ANALYZE decoded
---
THE SIMPLE TRUTH
90% of slow PostgreSQL:
β Missing index
β Fix: CREATE INDEX
9% of slow PostgreSQL:
β Wrong query pattern
β Fix: Rewrite query
1% of slow PostgreSQL:
β Actually needs tuning
β Fix: postgresql.conf
Start with indexes. Always.
---
DID YOU TRY IT?
- β‘ Added an index, saw improvement
- π Ran EXPLAIN ANALYZE, learned something
- π Just reading for now
- π€· My queries are fine (lucky you)
---
NEXT WEEK
Week 4. What do you want?
Options:
A) Full-text search (replace Algolia)
B) JSONB patterns (flexible schemas)
C) Database backups (don't lose everything)
D) Something else?
Reply with A, B, C, or your idea.
Most votes wins.
---
JANUARY PROGRESS
Week 1: Auth β
Week 2: Background jobs β
Week 3: Performance β
Week 4: You decide
---
See you Monday.
#January2026
β€1
π YOU DON'T NEED ALGOLIA
"We need search. Let's add Algolia."
Algolia pricing:
- Free: 10K requests/month
- Grow: $1 per 1K requests
- Real usage: $50-500/month
PostgreSQL pricing:
- $0
- Forever
---
WHAT ALGOLIA DOES
- Full-text search
- Typo tolerance
- Ranking
- Fast responses
WHAT POSTGRESQL DOES
- Full-text search β
- Typo tolerance (with pg_trgm) β
- Ranking β
- Fast responses β
The difference? Marketing budget.
---
WHEN YOU ACTUALLY NEED ALGOLIA
- Searching millions of documents
- Sub-10ms response required
- Complex faceting and filters
- You have the budget
WHEN POSTGRESQL IS ENOUGH
- Searching thousands to hundreds of thousands
- <100ms response is fine
- Basic to moderate filtering
- You'd rather not pay
That's most of us.
---
THIS WEEK
Tue: PostgreSQL search fundamentals
Wed: π° Copy-paste search system (3 β)
Thu: Fuzzy matching with pg_trgm
Fri: January retro
---
Last week's vote: Search won.
Let's build it.
#January2026
"We need search. Let's add Algolia."
Algolia pricing:
- Free: 10K requests/month
- Grow: $1 per 1K requests
- Real usage: $50-500/month
PostgreSQL pricing:
- $0
- Forever
---
WHAT ALGOLIA DOES
- Full-text search
- Typo tolerance
- Ranking
- Fast responses
WHAT POSTGRESQL DOES
- Full-text search β
- Typo tolerance (with pg_trgm) β
- Ranking β
- Fast responses β
The difference? Marketing budget.
---
WHEN YOU ACTUALLY NEED ALGOLIA
- Searching millions of documents
- Sub-10ms response required
- Complex faceting and filters
- You have the budget
WHEN POSTGRESQL IS ENOUGH
- Searching thousands to hundreds of thousands
- <100ms response is fine
- Basic to moderate filtering
- You'd rather not pay
That's most of us.
---
THIS WEEK
Tue: PostgreSQL search fundamentals
Wed: π° Copy-paste search system (3 β)
Thu: Fuzzy matching with pg_trgm
Fri: January retro
---
Last week's vote: Search won.
Let's build it.
#January2026
2.48Kβ€1