<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Articles on pgmi</title><link>https://vvka-141.github.io/pgmi/articles/</link><description>Recent content in Articles on pgmi</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 26 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://vvka-141.github.io/pgmi/articles/index.xml" rel="self" type="application/rss+xml"/><item><title>Your Deployment Is a PostgreSQL Program</title><link>https://vvka-141.github.io/pgmi/articles/deployment-is-a-postgresql-program/</link><pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/deployment-is-a-postgresql-program/</guid><description>&lt;h1 id="your-deployment-is-a-postgresql-program"&gt;Your Deployment Is a PostgreSQL Program&lt;a class="anchor" href="#your-deployment-is-a-postgresql-program"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Every migration tool is a program that executes your SQL. Invert the control flow and deployment policy becomes SQL the project owns.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Abstract.&lt;/strong&gt; Every team that adopts a migration tool eventually goes looking for a flag.
The release needs one thing the tool did not anticipate — a check that must run after the
schema change but before the commit, a concurrent index built in the middle of an
otherwise transactional deployment, an ordering rule that filenames cannot express — and
the search begins: through the configuration reference, then the issue tracker, then the
changelog of a version that has not shipped. The flag is the visible symptom of an
invisible arrangement. A migration tool is a program that executes your SQL, which means
every deployment semantic — what runs, in what order, inside which transaction, and
whether the result is allowed to commit — belongs to the tool&amp;rsquo;s vocabulary. Anything
outside that vocabulary is a feature request. This article describes the inversion: a tool
that prepares one PostgreSQL session, materializes the project as relations inside it, and
hands deployment policy to a SQL program the project owns. The tool keeps the execution
mechanism; the project takes the policy. What used to require a tool feature becomes SQL the
project owns.
The scope is the database semantics — choosing and ordering the work, controlling its
transaction boundaries, validating the result, and deciding whether it may commit — and not
the cloud APIs, secret stores, and approval gates around them. The costs are specific too, and
the last part names them.&lt;/p&gt;</description></item><item><title>Scenario-Tree Testing in PostgreSQL: Every Authored Branch, Shared History, Before COMMIT</title><link>https://vvka-141.github.io/pgmi/articles/test-every-branch-before-commit/</link><pubDate>Tue, 25 Aug 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/test-every-branch-before-commit/</guid><description>&lt;h1 id="scenario-tree-testing-in-postgresql-every-authored-branch-shared-history-before-commit"&gt;Scenario-Tree Testing in PostgreSQL: Every Authored Branch, Shared History, Before COMMIT&lt;a class="anchor" href="#scenario-tree-testing-in-postgresql-every-authored-branch-shared-history-before-commit"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Express the branching scenarios of your business logic as a directory tree; walk it with
savepoints so each branch inherits its history instead of rebuilding it; and let the walk
decide whether your deployment commits.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Abstract.&lt;/strong&gt; Database tests often repeat the same state-building work, because several
scenarios share the same prefix: a device may be provisioned before testing its
configuration paths; an order may be paid before testing shipment and refund; a workflow
may be approved before testing its downstream outcomes. The running example throughout is
an order lifecycle — chosen only because its branching states are easy to see, and standing
in for whatever lifecycle your own database implements. To test both &lt;code&gt;placed → paid → shipped&lt;/code&gt; and
&lt;code&gt;placed → paid → refunded&lt;/code&gt;, a conventional suite constructs &lt;code&gt;placed → paid&lt;/code&gt; twice. This
article develops the alternative: express the scenarios as a directory tree and walk it
with PostgreSQL savepoints — execute the shared prefix once, test one branch, roll back to
the branch point, and test its sibling from the same inherited state. Every scenario then
runs against the accumulated state it actually depends on, without rebuilding that state
and without seeing a sibling&amp;rsquo;s changes. A lifecycle&amp;rsquo;s reachable histories branch like a
multiverse, far beyond what a practical suite can cover, so the tree is authored, not
exhaustive: you choose the critical paths, and the walk &lt;em&gt;proves&lt;/em&gt; each one from the exact
parent state it depends on — proof here meaning execution plus declared-invariant checks,
not formal verification. And because most PostgreSQL DDL is transactional, the whole walk can run inside a
still-uncommitted deployment: apply the migration, run the tree, discard the test state,
and commit only if every authored scenario passes.&lt;/p&gt;</description></item><item><title>The Request Becomes a Transaction</title><link>https://vvka-141.github.io/pgmi/articles/request-becomes-a-transaction/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/request-becomes-a-transaction/</guid><description>&lt;h1 id="the-request-becomes-a-transaction"&gt;The Request Becomes a Transaction&lt;a class="anchor" href="#the-request-becomes-a-transaction"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;A transactional API has two halves: a network edge and a transactional operation. Keep the first at that edge; give the second to PostgreSQL, where its authority already lives — and every declared outcome of every operation becomes provable in the same transaction.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Abstract.&lt;/strong&gt; The last five years consolidated storage into PostgreSQL: the queue, the cache, the search index, and the vector store moved in, one &amp;ldquo;just use Postgres&amp;rdquo; argument at a time. The API tier did not move — and the debate over whether it should is usually fought across the wrong boundary. A transactional API has two halves. The network edge authenticates the caller and adapts HTTP. The transactional boundary resolves the operation, validates its input, authorizes it against current state, executes the transition, and shapes the result. Convention puts the first half in a gateway and the second in an application framework — even though every authoritative decision in the second half already terminates in PostgreSQL. This article moves that boundary to where its authority lives, focusing on APIs whose valuable behavior is transactional decision-making over PostgreSQL state. The unit of design becomes the &lt;em&gt;transactional operation&lt;/em&gt;: a named database operation with a typed contract, an authorization policy, a declared transaction, an implementation, and tests — and each protocol surface, starting with REST, becomes a binding to it. The payoff is one authority, one transaction, one executable proof: a test can invoke an operation end to end, assert on the response and the state transition in the same snapshot, and roll everything back.&lt;/p&gt;</description></item><item><title>Your ALTER TABLE Is Fast. The Queue Behind It Is Not.</title><link>https://vvka-141.github.io/pgmi/articles/lock-queue-fast-is-not-safe/</link><pubDate>Sat, 01 Aug 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/lock-queue-fast-is-not-safe/</guid><description>&lt;h1 id="your-alter-table-is-fast-the-queue-behind-it-is-not"&gt;Your ALTER TABLE Is Fast. The Queue Behind It Is Not.&lt;a class="anchor" href="#your-alter-table-is-fast-the-queue-behind-it-is-not"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;A schema change that runs in six milliseconds can still stop every read on the table for sixteen seconds — the damage is done by waiting, not by working.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is the same statement, run twice against the same table — PostgreSQL 16.14
in a local container, an &lt;code&gt;orders&lt;/code&gt; table of 100,000 rows, the contending session
held open with &lt;code&gt;pg_sleep&lt;/code&gt;. These are illustrative numbers from one machine, not
a benchmark; the ratio is the point, and you can reproduce it in about a minute.&lt;/p&gt;</description></item><item><title>Your Transaction Boundary Belongs in the Program, Not the Filename</title><link>https://vvka-141.github.io/pgmi/articles/transaction-boundary-in-the-program/</link><pubDate>Sat, 01 Aug 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/transaction-boundary-in-the-program/</guid><description>&lt;h1 id="your-transaction-boundary-belongs-in-the-program-not-the-filename"&gt;Your Transaction Boundary Belongs in the Program, Not the Filename&lt;a class="anchor" href="#your-transaction-boundary-belongs-in-the-program-not-the-filename"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;A phased schema change spans transactional and non-transactional work. Most migration frameworks express that distinction as metadata; pgmi makes it part of the SQL program.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Three facts about PostgreSQL decide how a deployment has to be shaped, and all
three are the server&amp;rsquo;s rules rather than any tool&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;A table-level lock is held until the end of the transaction that took it, not
until the statement finishes — so a six-millisecond &lt;code&gt;ALTER TABLE&lt;/code&gt; at the top of
a five-minute transaction blocks readers for five minutes. &lt;code&gt;lock_timeout&lt;/code&gt;
bounds how long a statement &lt;em&gt;waits&lt;/em&gt; for a lock, which is a different quantity
again. And &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, the statement you reach for precisely
because it does not block reads or writes, refuses to run inside a transaction
block at all — and refuses a second way, from inside any function, procedure or
&lt;code&gt;DO&lt;/code&gt; block, with a different error:&lt;/p&gt;</description></item><item><title>Your Migration Numbers Are a Distributed Counter Without Coordination</title><link>https://vvka-141.github.io/pgmi/articles/migration-numbers-distributed-counter/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/migration-numbers-distributed-counter/</guid><description>&lt;h1 id="your-migration-numbers-are-a-distributed-counter-without-coordination"&gt;Your Migration Numbers Are a Distributed Counter Without Coordination&lt;a class="anchor" href="#your-migration-numbers-are-a-distributed-counter-without-coordination"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;What migration-number collisions reveal about identity, ordering, and enforcement.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Two engineers branch from the same commit on the same Tuesday. Each adds a
migration; each picks the next free number — &lt;code&gt;V42&lt;/code&gt;. Git merges both files
without complaint: two new files, no textual conflict. The migration runner
cannot merge them. Two changes now claim the same position.&lt;/p&gt;
&lt;p&gt;Timestamp versions make that exact collision unlikely, but trade it for a
subtler problem: late arrival. A migration stamped earlier can merge &lt;em&gt;after&lt;/em&gt; a
later one has already run in some environment. Flyway ignores the straggler by
default; its &lt;a href="https://documentation.red-gate.com/fd/flyway-out-of-order-setting-277579015.html"&gt;&lt;code&gt;outOfOrder&lt;/code&gt;&lt;/a&gt;

setting applies it — &amp;ldquo;a newly discovered version 2.0 will be executed rather
than ignored&amp;rdquo; — at the cost of an application history that now differs from a
freshly built environment. If you have run migrations on a team
of more than two people you have lived some version of this, and lived the
fixes: timestamp prefixes, &lt;code&gt;outOfOrder&lt;/code&gt;, renumbering commits, &amp;ldquo;announce in
Slack before you add a migration.&amp;rdquo;&lt;/p&gt;</description></item><item><title>From Seed Scripts to Desired-State Reference Data in PostgreSQL</title><link>https://vvka-141.github.io/pgmi/articles/desired-state-reference-data-postgresql/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/desired-state-reference-data-postgresql/</guid><description>&lt;h1 id="from-seed-scripts-to-desired-state-reference-data-in-postgresql"&gt;From Seed Scripts to Desired-State Reference Data in PostgreSQL&lt;a class="anchor" href="#from-seed-scripts-to-desired-state-reference-data-in-postgresql"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Version the catalog that should exist — not the procedure that inserts it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A seed script records &lt;em&gt;how&lt;/em&gt; to construct a catalog. What you need to version is
the catalog that &lt;em&gt;should exist&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The difference barely matters when &lt;code&gt;seed.sql&lt;/code&gt; is twelve lookup rows. It starts to
matter once roles reference permissions, workflow states reference their legal
transitions, and plan tiers reference feature flags — once every edit has to
preserve foreign-key order by hand. At that point the script has quietly become a
hand-maintained topological sort of your data model: an acyclic insert-dependency
graph, revised on every new relationship and enforced by nothing but SQLSTATE
23503 at deploy time.&lt;/p&gt;</description></item><item><title>AI agents write PostgreSQL like Python</title><link>https://vvka-141.github.io/pgmi/articles/ai-agents-write-postgresql-like-python/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/ai-agents-write-postgresql-like-python/</guid><description>&lt;h1 id="ai-agents-write-postgresql-like-python"&gt;AI agents write PostgreSQL like Python&lt;a class="anchor" href="#ai-agents-write-postgresql-like-python"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;The Python habits agents carry into PL/pgSQL — exceptions as control flow, casts as trust, validation scattered everywhere — and the handler discipline that contains them.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Give a coding agent a PostgreSQL-first backend to build and it will write you working SQL. It will also, with remarkable consistency, write you &lt;em&gt;Python&lt;/em&gt; — Python&amp;rsquo;s exception-driven control flow, Python&amp;rsquo;s trust in casts, Python&amp;rsquo;s habit of validating wherever the code happens to be — transliterated into PL/pgSQL, where those idioms carry costs the agent never sees.&lt;/p&gt;</description></item><item><title>Test PostgreSQL migrations before COMMIT</title><link>https://vvka-141.github.io/pgmi/articles/test-postgresql-migrations-before-commit/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><guid>https://vvka-141.github.io/pgmi/articles/test-postgresql-migrations-before-commit/</guid><description>&lt;h1 id="test-postgresql-migrations-before-commit"&gt;Test PostgreSQL migrations before COMMIT&lt;a class="anchor" href="#test-postgresql-migrations-before-commit"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Test a migration after it applies but before it commits — and let &lt;code&gt;COMMIT&lt;/code&gt; depend on the checks.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By Alexey Evlampiev&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Most migration pipelines test before deployment or after commit. PostgreSQL
allows a third checkpoint: &lt;strong&gt;after applying the change, but before committing
it&lt;/strong&gt;. Lint and rehearsal catch what they can in advance; integration suites
catch what they can afterwards. The checkpoint in between is the only one
where the real target database — with whatever drift it has accumulated — is
in its migrated state while rollback is still one statement away.&lt;/p&gt;</description></item></channel></rss>