Skip to content

Archive

Databases

36 articles
Software Engineering 13 Sep 2026 7 min read

Schema Changes Are Multi-Version Protocols

A column rename looks atomic in a schema diff. A deployed system rarely experiences it that way. During a rolling release, old application processes can remain active after new processes start. Background jobs may run code built from another release. Replicas can lag behind a primary. Queued work can outlive the binary that created it. Data written before the change remains present after the new schema exists. The migration therefore crosses several versions of code and data at once.

Software Engineering 13 Sep 2026 7 min read

Keyset Pagination Under Concurrent Writes

Keyset Pagination Under Concurrent Writes A query returns twenty rows ordered by creation time. Before the client asks for the next twenty, another transaction inserts a row near the front of that order. The data set has changed, but the client still expects page two to continue from the point page one reached. That expectation exposes the main difference between offset pagination and keyset pagination. An offset identifies a position in a particular query result. A keyset cursor identifies an ordering boundary. Under concurrent writes, those are not equivalent references.

Software Engineering 12 Sep 2026 8 min read

Write-Ahead Logging and the Meaning of Commit

A database can report a transaction as committed while the data pages touched by that transaction are still absent from their final locations on disk. That behavior is not a contradiction. In systems built around write-ahead logging, durability is established by the log before the modified pages need to reach durable storage. The distinction matters because a transaction changes several kinds of state at once. It changes the logical database, it changes in-memory page images, and it creates recovery information. Treating those as a single physical write obscures the mechanism that gives commit its meaning after a crash.

Software Engineering 12 Sep 2026 10 min read

Prevent Write Skew with Serializable Transactions

Database transactions make many state changes easier to reason about, but transaction boundaries alone do not guarantee that every business invariant survives concurrency. A particularly subtle failure is write skew: two transactions read overlapping state, update different rows, and both commit even though their combined result violates a rule. This anomaly matters because each transaction can look correct in isolation. The defect appears only when valid decisions are made from snapshots that become incompatible once both writes are accepted.

Software Engineering 12 Sep 2026 10 min read

Phantom Rows and the Limits of Row-Level Locking

A transaction can lock every row it reads and still leave a business rule exposed. The gap appears when the rule is about a set described by a predicate, not only the rows that currently satisfy it. Suppose an application limits a small allocation group to four active reservations. A transaction queries the active rows, sees three, and decides that one more reservation is valid. If another transaction inserts a new matching row before the first transaction commits, both decisions may have been based on a set that no longer represents the committed state.

Software Engineering 12 Sep 2026 9 min read

Optimistic Concurrency with Version Columns

A row can be read correctly, modified correctly, and still be written incorrectly. The problem appears when another transaction changes the same logical record between the read and the write. A plain UPDATE often has no memory of the state on which the new values were based, so the later writer can replace an earlier change without detecting the race. A version column turns that hidden assumption into a predicate. The update says, in effect, that the write is valid only while the row remains at the version that the caller observed. The database then evaluates the state check and the mutation as one atomic statement.

Software Engineering 11 Sep 2026 8 min read

Expand and Contract Database Changes for Safe Deployments

Expand and Contract Database Changes for Safe Deployments A database schema can change in milliseconds while an application fleet takes minutes or hours to converge on a new version. During that interval, old and new application instances may use the same database at the same time. That overlap turns an ordinary schema edit into a compatibility problem. Renaming a column in one migration, for example, can break old instances immediately even when the new application code is correct.

Python 09 Sep 2026 13 min read

Migrate Time-Based Identifiers from UUIDv1 to UUIDv6 in Python 3.14

UUID version 1 has been around for a long time. It combines a timestamp, a clock sequence, and a node identifier into a 128-bit value, which makes it useful when applications need identifiers that can be generated without coordinating through a central database sequence. Its layout has an awkward property, though: the timestamp bits are not arranged from most significant to least significant in the same order that ordinary UUID comparison uses.

Python 08 Sep 2026 9 min read

Use UUIDv7 for Time-Ordered Identifiers in Python

Python 3.14 added uuid.uuid7(), giving applications a standard-library way to generate UUID version 7 identifiers defined by RFC 9562. UUIDv7 is useful when an application wants a globally shaped 128-bit identifier while also putting creation time near the front of the identifier. That property can make newly generated values naturally cluster by time in systems that sort UUIDs by their binary or canonical value. It is tempting to summarize UUIDv7 as “a sortable UUID.” That is directionally useful but incomplete. The timestamp has millisecond resolution, Python adds a counter for monotonicity within a millisecond, clocks can move, and separate processes do not become a distributed sequence generator merely because they all use UUIDv7.

Python 08 Sep 2026 7 min read

Generate Time-Ordered IDs with Python UUIDv7

Random UUIDs are convenient identifiers: they can be generated without coordinating with a database, and the probability of collision is tiny. But a UUIDv4 primary key has one awkward property for ordered indexes: newly generated values are spread across the key space instead of tending toward the end of the index. UUID version 7 keeps the decentralized 128-bit UUID shape while putting a Unix-epoch millisecond timestamp at the front. Python 3.14 adds uuid.uuid7() to the standard library, so applications no longer need a third-party package just to generate RFC 9562 UUIDv7 values.

Web Development 01 Sep 2026 5 min read

Safe Database Migrations with the Expand-and-Contract Pattern

A schema migration can be syntactically correct and still cause an outage. Production deployments often run old and new application versions at the same time, background workers may lag behind, and a large table can turn a simple-looking DDL statement into a long lock. The expand-and-contract pattern reduces those risks by splitting an incompatible change into compatible stages. Why one-step schema changes are risky Suppose an application wants to rename users.full_name to users.display_name.

Web Development 01 Sep 2026 7 min read

Keyset Pagination in SQL for Fast, Stable APIs

Pagination looks simple until a table becomes large or new rows are inserted while a client is paging through results. LIMIT and OFFSET are easy to understand, but deep offsets can become expensive and changing data can make rows appear twice or disappear between requests. Keyset pagination, also called seek pagination, avoids those problems by asking for rows after a known position instead of asking the database to skip a number of rows.