<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go Database Patterns on Atharva Pandey</title><link>https://atharva.page/series/go-database-patterns/</link><description>Recent content in Go Database Patterns on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Wed, 18 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/go-database-patterns/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 9: Testing with Real Databases — Mocking sql.DB is lying to yourself</title><link>https://atharva.page/post/go/go-db-testing/</link><pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-testing/</guid><description>&lt;p&gt;For years, the standard advice for testing Go database code was to use &lt;code&gt;sqlmock&lt;/code&gt; — a library that intercepts database calls and lets you assert which queries were run. I used it for a while. Then I started finding production bugs that my &lt;code&gt;sqlmock&lt;/code&gt; tests were actively hiding: constraint violations that only happen with real Postgres, query plan differences, JSON operator behavior, NULL handling edge cases, transaction isolation behavior. &lt;code&gt;sqlmock&lt;/code&gt; tests were passing while the same code was failing in production. That&amp;rsquo;s worse than no tests at all.&lt;/p&gt;</description></item><item><title>Lesson 8: Migrations Without Downtime — ALTER TABLE can lock your entire database</title><link>https://atharva.page/post/go/go-db-migrations/</link><pubDate>Tue, 27 Jan 2026 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-migrations/</guid><description>&lt;p&gt;The first time I caused a production outage with a database migration, I was adding a &lt;code&gt;NOT NULL&lt;/code&gt; column to a table. The migration looked innocent. It ran fine on staging with 1,000 rows. In production with 40 million rows, it locked the entire table for 11 minutes while Postgres rewrote every row. Every write to that table failed with a lock timeout. We rolled back the app but couldn&amp;rsquo;t roll back the migration. It was a terrible morning.&lt;/p&gt;</description></item><item><title>Lesson 7: The N+1 Problem — One query per row is a performance bug</title><link>https://atharva.page/post/go/go-db-n-plus-one/</link><pubDate>Thu, 25 Dec 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-n-plus-one/</guid><description>&lt;p&gt;The N+1 problem is the most common database performance issue I find when reviewing Go code, and it&amp;rsquo;s especially sneaky because it looks totally fine in development. You have a list of 10 users, you fetch their orders, 11 queries, no problem. You deploy to production, the table has 50,000 users, your endpoint suddenly takes 40 seconds, and you get a 3am page. The queries were always there — you just didn&amp;rsquo;t notice them until the data grew.&lt;/p&gt;</description></item><item><title>Lesson 6: Context with Database Queries — Every query needs a timeout</title><link>https://atharva.page/post/go/go-db-context-queries/</link><pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-context-queries/</guid><description>&lt;p&gt;We once had a query that ran for 47 minutes. I&amp;rsquo;m not joking. A reporting query that worked fine on the test dataset decided to do a full sequential scan on a 200M-row table in production because someone dropped an index by accident the night before. The query just ran. And ran. And ran. The connection was held the whole time, blocking the pool. New requests started queuing. Within 10 minutes, the service was effectively down — not because of an error, but because every database connection was held by queries waiting for that one slow one to finish.&lt;/p&gt;</description></item><item><title>Lesson 5: sqlc vs ORM vs Raw SQL — Pick your tradeoff, not your religion</title><link>https://atharva.page/post/go/go-db-sqlc-vs-orm/</link><pubDate>Wed, 29 Oct 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-sqlc-vs-orm/</guid><description>&lt;p&gt;Every time someone asks &amp;ldquo;should I use GORM or raw SQL?&amp;rdquo; a flame war breaks out. I&amp;rsquo;ve been on both sides of that argument, and I&amp;rsquo;ve shipped production systems using all four approaches — raw SQL, GORM, sqlc, and Ent. My opinion now is boring: each one is the right choice in a specific context, and none of them is universally correct. The question isn&amp;rsquo;t which one is best, it&amp;rsquo;s which tradeoffs you&amp;rsquo;re signing up for.&lt;/p&gt;</description></item><item><title>Lesson 4: The Repository Pattern — Sometimes a function is enough</title><link>https://atharva.page/post/go/go-db-repository-pattern/</link><pubDate>Thu, 02 Oct 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-repository-pattern/</guid><description>&lt;p&gt;The repository pattern is one of those ideas that sounds great in an architecture talk and causes real pain when applied indiscriminately to a Go codebase. I&amp;rsquo;ve seen teams add a &lt;code&gt;UserRepository&lt;/code&gt; interface with five methods, a concrete &lt;code&gt;postgresUserRepository&lt;/code&gt; implementation, and a &lt;code&gt;mockUserRepository&lt;/code&gt; for tests — and then wonder why everything takes three times as long to write. I&amp;rsquo;ve also seen teams skip the pattern entirely and end up with &lt;code&gt;*sql.DB&lt;/code&gt; threaded through 40 different functions, impossible to test without a real database.&lt;/p&gt;</description></item><item><title>Lesson 3: Transactions That Don't Bite — Begin, defer rollback, commit</title><link>https://atharva.page/post/go/go-db-transactions/</link><pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-transactions/</guid><description>&lt;p&gt;Transactions are the part of database programming where &amp;ldquo;it&amp;rsquo;s fine most of the time&amp;rdquo; really isn&amp;rsquo;t good enough. A buggy SELECT just returns wrong data. A buggy transaction can leave your database in a half-written state — an order placed without inventory decremented, money debited without the transfer completing, a user created without their profile record. The bugs are subtle, often don&amp;rsquo;t manifest in testing, and only show up in production when two things happen at the same time.&lt;/p&gt;</description></item><item><title>Lesson 2: Connection Pool Tuning — Your pool config is probably wrong</title><link>https://atharva.page/post/go/go-db-connection-pools/</link><pubDate>Fri, 08 Aug 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-connection-pools/</guid><description>&lt;p&gt;Here&amp;rsquo;s a scenario that played out for me once: service is running fine for weeks, then we double traffic, and suddenly requests start timing out. Not all of them — maybe 10%. The logs show &lt;code&gt;pq: sorry, too many clients already&lt;/code&gt;. Postgres is refusing connections. But we have a connection pool — that&amp;rsquo;s the whole point, right? Why is Postgres seeing more connections than it can handle?&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;database/sql&lt;/code&gt;&amp;rsquo;s default pool settings are almost certainly wrong for production, and most people never change them.&lt;/p&gt;</description></item><item><title>Lesson 1: database/sql Done Right — The stdlib is better than you think</title><link>https://atharva.page/post/go/go-db-sql-done-right/</link><pubDate>Tue, 08 Jul 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-db-sql-done-right/</guid><description>&lt;p&gt;I spent a long time reaching for third-party database libraries in Go before I actually read the &lt;code&gt;database/sql&lt;/code&gt; docs. When I finally did, I was embarrassed — the stdlib had everything I needed and I&amp;rsquo;d been adding dependencies for no reason. The problem isn&amp;rsquo;t that &lt;code&gt;database/sql&lt;/code&gt; is limited. The problem is that it has a handful of non-obvious behaviors that, if you don&amp;rsquo;t know about them, will burn you in production. Once you internalize those, you&amp;rsquo;ll write better database code than most people using ORMs.&lt;/p&gt;</description></item></channel></rss>