Two Cloudflare deep dives on running systems at scale, plus Kent Beck on splitting tests and Jim Highsmith on how engineers should write.
Picked from 56 new articles across 34 sources · arrow keys for other days
Today in 30 seconds
30s left
Cloudflare runs each D1 database as a SQLite Durable Object, and a heartbeat every 30 seconds keeps those queries off its central Postgres. story 1 ↓
The same heartbeats repair failed creations, stale routing entries and usage-limit state, using idempotent retries and a single writer. story 1 ↓
Cloudflare cut 70 GB/s of backbone traffic by sending each event once, sampling at the source and aggregating at every hop. story 2 ↓
Kent Beck's republished 2013 recipe splits long, chained tests into focused ones, so a failure points at its cause without losing coverage. story 3 ↓
Jim Highsmith's "Practitioner Voice" for engineers: open with your conclusion, show your failures, and edit in three separate passes. story 4 ↓
1/4Cloudflare· David Laban· Oct 13
How Cloudflare keeps millions of D1 databases' queries off central Postgres
Cloudflare explains how D1, its SQLite-based database service, handles millions of databases. Queries never touch the central Postgres. Metadata lives in edge caches, and periodic heartbeats repair partial failures. Usage limits run on eventually consistent queues.
Why it matters · These patterns carry over to any multi-tenant control plane: idempotent retries, a single owner for writes to a weakly consistent store, pre-aggregated counters, and cache keys built to be shared.
Know this first
Context from the editor, not the article · 32 sec
1 · Control plane vs data plane
The data plane serves user traffic, here SQL queries, and must be fast and always available. The control plane handles management: creating and deleting databases, metadata and limits. A common failure mode is a data plane that quietly depends on a central control-plane database.
2 · Durable Objects
Cloudflare Durable Objects are small single-threaded actors, each with its own embedded storage, placed in one data center. Requests for a given object ID always reach that one instance, so it can serialize all operations on its own state.
3 · Why many small databases
2/4Cloudflare· Cathy Chi· Oct 13
How Cloudflare cut 70 GB/s from its data pipeline's backbone traffic
Cloudflare's data pipeline carries about two billion events per second. In 2026 its Data team removed about 70 GB/s of compressed backbone traffic. It sent each event once instead of twice, sampled data at the source, and pre-aggregated it at every hop.
Why it matters · It shows concrete ways to keep exponential data growth off your hardware budget: move replication later in the pipeline, reduce data earlier, and measure what each step saves.
Know this first
Context from the editor, not the article · 32 sec
1 · Edge-to-core log pipelines
Large networks produce event records on thousands of edge servers. Those records must travel over wide-area links to a few central data centers, where they are buffered, processed and stored for queries. The links between sites, called the backbone, are expensive, and traffic on them grows with every new request served.
2 · ClickHouse and rollups
ClickHouse is a column-oriented analytics database built for fast queries over huge append-only tables. Its aggregating table engines can merge rows with matching keys into running sums and counts. Those stored aggregates are called rollups.
3 · LZ4 versus zstd
3/4Kent Beck (Tidy First?)· Kent Beck· Oct 13
Kent Beck's four-step recipe for splitting long tests into specific ones
Kent Beck describes "run-on tests", single tests that chain several operations and assertions, and gives a step-by-step way to split them into short, well-named tests that cover exactly as much as the original.
Why it matters · Run-on tests turn a red build into a debugging session. This is a cheap, mechanical refactoring that makes failures point at the cause.
Know this first
Context from the editor, not the article · 24 sec
1 · Who Kent Beck is
Kent Beck created Extreme Programming and popularized test-driven development (TDD), where you write a failing test first and then the code to pass it. He also co-created JUnit, the Java testing framework behind most xUnit-style tools. His newsletter is called Tidy First?
2 · Test Desiderata
Test Desiderata is Beck's list of properties good tests should have, such as isolated, fast, readable and deterministic. This piece relies on two of them. "Specific" means a failure points clearly at its cause. "Composable" means tests can each check one thing and still add up to full coverage.
Image: Kent Beck (Tidy First?)
4/4Martin Fowler· Martin Fowler· Oct 13
Jim Highsmith proposes "Practitioner Voice" as a way for engineers to write
Jim Highsmith, writing on martinfowler.com, names a style he calls Practitioner Voice: writing whose authority comes from experience you were accountable for. He gives concrete rules for structure, editing and word choice.
Why it matters · Senior engineers write design docs, postmortems and essays, and Highsmith offers specific, checkable writing habits instead of vague advice to "be clearer."
Know this first
Context from the editor, not the article · 22 sec
1 · Who Jim Highsmith is
Highsmith co-wrote the 2001 Agile Manifesto and wrote books including Adaptive Software Development and Agile Project Management. This essay appears on martinfowler.com, the long-running site of Martin Fowler, another Manifesto author, who once told Highsmith to put more of himself into a manuscript.
2 · The two default registers
Most professional writing takes one of two forms. Academic writing builds toward a hedged conclusion through citations and caveats. "Thought leadership" content is polished, brand-driven writing, often a hook followed by three takeaways. Highsmith argues that both hide the author's own judgment.
Multi-tenant platforms increasingly give each user, app or preview environment its own database. You get millions of tiny, mostly idle databases instead of a few big clusters. Per-database overhead and per-account aggregate queries then become the main scaling problem.
D1 components: Edge API and Eyeball Workers, Durable Objects, Workers KV, and central Postgres via Hyperdrive.1 / 4
Cloudflare's David Laban describes how 1, Cloudflare's managed SQLite database service, now supports millions of databases, hundreds of thousands of them active on any day. Live databases grew 15x in a year. The constraint the team designed around: no query may depend on a central, single-region component.
The setup
Each database is one SQLite 2 (DO), a Cloudflare actor with its own storage running in one data center. An Edge API Worker (a Cloudflare serverless function) serves the HTTP API. An Eyeball Worker proxies all traffic to DOs. Metadata lives in a central Postgres cluster, reached through 3, a pooling and caching proxy. Routing data sits in 4, an eventually consistent global key-value store.
Creation without a big transaction
Wrapping DO setup in a Postgres transaction would hold a connection open while a possibly distant DO initializes. So creation runs as ordered, retryable steps:
Check the account limit against a cached replica.
Initialize a DO near the user.
Write the Postgres row. That write is the checkpoint: once it lands, the database exists.
Write KV on a best-effort basis.
Each retry uses fresh database and DO IDs. If DO A initializes but its Postgres write fails, the retry creates DO B. Only B is recorded.
Heartbeats fix the rest
Every DO checks in every 30 seconds, with jitter. Orphan A learns it doesn't exist and deletes itself. KV has no consistent reads, so each DO is the only writer of its own KV entry, and the heartbeat rewrites the entry if the earlier write failed.
The query path
HTTP queries look up routing in KV and fall back to Hyperdrive's caches, then a replica. Queries from Worker bindings skip the lookup. Laban argues KV is needed because it is durable, not just a cache: a mass cache purge sending a million databases back to Postgres would cause a 5, where retry load keeps the system down.
Limits, eventually consistent
Storage usage flows through a queue with a capped number of consumers. A trigger logs per-database deltas, and a job runs every second that drains the log with DELETE ... RETURNING and adds one summed delta per account. This avoids both linear SUM scans and contention on hot rows. Heartbeats read the total with the account ID as the only cache-key parameter, so all of an account's databases share one cached value. A time-bucket parameter forces a shorter TTL.
Free-tier row limits use ClickHouse, a columnar analytics database, plus 6, Cloudflare's fast-replicating internal key-value store. A central job flags over-limit accounts, and DOs refuse queries until midnight. The post gives no latency or load-test figures.
Cloudflare's 1 is a managed, serverless database built on SQLite. A post by David Laban describes how its control plane was rebuilt to handle millions of created databases, hundreds of thousands of them active on any given day. Live databases grew 15x in a year and 65x in two years. Daily active databases grew 18x in a year. The design goal: grow the number of databases without routing any query through a central, single-region component.
The pieces
Each D1 database is backed by exactly one SQLite 2 (DO), Cloudflare's single-instance stateful actor with its own embedded storage. Several Workers (Cloudflare's serverless functions) sit around it:
The Edge API Worker serves the HTTP API, dashboard and CLIs. It handles authentication, authorization, rate limiting and validation.
The Eyeball Worker proxies every request that must reach a DO. It owns retries, error handling and observability.
A user's own Worker can hold a D1 binding: an encoded grant for one specific database that goes straight to the Eyeball Worker.
Metadata lives in a Postgres cluster in Cloudflare's central "Core" data centers. Workers reach it through 3, Cloudflare's connection-pooling and query-caching proxy for Postgres. One Hyperdrive configuration is cached and points at read replicas; the other is uncached and points at the primary. 4, a globally distributed, eventually consistent key-value store, holds routing metadata at the edge.
Every operation falls into one of three planes. The data plane (queries) must stay at the edge. The control plane (create, delete, list) may depend on central Postgres. The asynchronous plane (analytics, billing) may be eventually consistent.
Creating a database without a distributed transaction
Creating a database touches three stores: the new DO, Postgres and KV. A single Postgres transaction around all of it would hold a connection open while a DO initializes, possibly on the other side of the world. That doesn't scale, so D1 orders the steps and makes each one retryable:
Check the account's database count against its limit, using a cached read from a replica (TTL of a few seconds).
Forward the request to a data center near Postgres, because the endpoint makes several round trips. From afar each trip can cost hundreds of milliseconds; nearby it costs a few.
Steps 3–4 run in a retry loop, and every retry uses new IDs. A worked failure case: DO A initializes, then the Postgres write fails. The retry creates DO B and the write succeeds. Postgres, the source of truth, only knows about B. Reusing the database UUID would have left two DOs each believing they owned the same database. The DO is initialized before the Postgres write so that queries never reach an uninitialized object.
Heartbeats as the repair mechanism
Every DO sends a heartbeat to the control plane every 30 seconds, plus per-database jitter. The interval can be tuned per database or account, or turned off. The heartbeat fetches the database's state (active or deleted) and the account's limits. Slightly stale answers are fine, so Hyperdrive caches or replicas serve them. In the example above, orphan DO A learns within 30 seconds that its database doesn't exist and marks itself deleted.
Heartbeats also repair KV. KV offers no consistent reads, so D1 makes each DO the only writer of its own KV entry, which makes the DO the serialization point for that entry. If the best-effort write on create or delete fails, the next heartbeat fixes it. The DO also keeps its active/deleted state locally, so it rejects queries correctly even when KV is stale.
The query hot path
A query over HTTP runs the Edge API Worker near the caller. It reads the UUID-to-metadata mapping from KV. On a rare miss it falls back to Hyperdrive, which checks its cache in the caller's location, then its cache near Postgres, and only then a replica. It then calls the Eyeball Worker, which forwards the query to the DO. Queries from a binding skip the lookup entirely.
Why not address DOs by name and skip the mapping? Because the Eyeball Worker also needs metadata to choose the right DO namespace, so naming would turn one lookup into two. Laban still recommends naming as the default for most applications. And why KV when Hyperdrive already caches? KV is durable storage, not just a cache: a miss at the edge is served from KV's own storage, not Postgres. The post argues that a mass cache purge with a million databases refetching routes from Postgres would cause a 6, a state where retry load keeps the system overloaded after the original trigger is gone.
Storage limits: queues and pre-aggregation
The Free plan allows 5 GB of storage per account and Workers Paid allows 1 TB. DOs publish storage usage, aggregated per edge server, to a Kafka-like queue. A capped number of consumers in Core writes each database's latest size to database_stats. Capping the consumers caps the write load on Postgres.
Enforcement needs per-account totals, and SUM over millions of rows on every check would scale linearly with database count. Updating one total row per account on every write would cause lock contention instead. The fix uses three tables:
A trigger on database_stats writes each size delta to account_database_stats_changes, an append-only log.
A job runs every second with a 7 (common table expression). Its DELETE ... RETURNING atomically drains the log; the query then sums deltas by account and upserts one row per account into account_database_stats.
The result is an eventually consistent counter, cheaper than the built-in incremental view maintenance some databases provide. Heartbeats read it through Hyperdrive with the account ID as the only parameter, so every database in an account shares one cached result. To get a TTL shorter than Hyperdrive's 60-second default for this one query, they add a parameter equal to floor(now / X seconds), which changes the cache key every X seconds. Including the database ID would give each database a more exact allowance but would wreck the cache hit rate. They accept slower enforcement instead. Storage snapshots go to ClickHouse, a columnar analytics database, for history and billing.
Row limits: a different pattern
Free-tier daily row limits skip Postgres entirely. Each query emits a rows-read/rows-written event to ClickHouse. A central "Limiter" periodically runs a since-midnight GROUP BY account_id HAVING ... query. For each over-limit account it emails the owner (once per day) and sets a key in 8, Cloudflare's internal key-value store with sub-second global replication, set to expire at midnight. DOs check Quicksilver at the edge and refuse queries while the key exists. An upgrade arrives as a queue message and clears the key. The post says other limits may move to this pattern. It doesn't give load-test numbers, latencies or how stale enforcement gets in practice.
Remember
Instead of a distributed transaction: order the steps, retry with fresh IDs, and run periodic reconciliation that deletes orphans.
With a store that lacks consistent reads, make one owner the only writer for each key.
Choose cache keys so they are shared widely, even if it costs some precision, e.g., cache per account, not per database.
LZ4 and zstd are general-purpose compression algorithms. LZ4 is extremely fast but compresses less. Zstandard (zstd) usually gives noticeably smaller output at some extra CPU cost, and how much extra depends on the compression level chosen.
The pipeline: Logfwdr at the edge, central receivers, the Buftee buffer, then consumers such as ClickHouse.1 / 4
Cloudflare's Data team runs the pipeline that carries telemetry, about two billion events per second as of May 2026, from its global network to central data centers. Traffic grows exponentially while hardware grows linearly. The team estimates five years of fixes have deferred about 2.5 years of hardware spending. Three of those fixes shipped in 2026 and removed about 70 GB/s of compressed backbone traffic.
How the pipeline works
1, an agent on edge servers, collects events and sends them over wide-area links to central receivers. The receivers write to 2, a distributed buffer. Inserters then read from Buftee and write to 3, a column-oriented analytics database.
Earlier work
In 2022 the team split one monolithic message schema into per-product schemas, cutting bandwidth 35–40%. In 2024 it switched compression from 4 to 5, which cut compressed traffic nearly 40% with little measurable CPU cost.
Send once, replicate late
Before, Logfwdr sent two full copies of each analytics event across the wide-area network, one per region, so ClickHouse in each region had complete data. Now Logfwdr sends each event once, to any available receiver. "Remote inserters" copy the data from Buftee to the other region. If the link between regions drops, each side keeps ingesting and the replica catches up from Buftee's retention buffer. Transit fell by about 43 GB/s, while replication added only about 7 GB/s. The replicated data has already been sampled, and centrally batched data compresses better.
Sample at the source
Logfwdr now also samples. Each sampling key keeps the square root of its record count, and estimates are scaled back up by the sample rate. Busy keys shrink sharply while rare keys keep their detail. With two sampling stages, stored rows grow as x^0.319 of true volume instead of x^0.485. In one test, 11.6 million events took 18,000 rows instead of 79,000, with the same estimates. Three top datasets each halved, saving about 25 GB/s.
Aggregate at every hop
Rollups such as requests per domain per minute used to be computed only in the inserters. Now partial aggregates are merged at Logfwdr, at the receivers, at the inserters and in ClickHouse. The HTTP rollup dataset fell from over 9 GB/s to under 2 GB/s.
Results
Load balancing halved Ready-Analytics, an internal analytics product, in May. Growth took back half of that saving within two months, and sampling then brought it to about 8 GB/s. Its receivers freed about 750 CPUs and 3.5 TiB of memory. The trade-offs: sampling gives approximate answers, 6 need their dimensions chosen in advance, and the replica lags during backbone outages.
The news
Cloudflare's Data team runs the pipeline that moves telemetry from Cloudflare's global network to its central data centers. In May 2026 that pipeline delivered about two billion events per second. The data feeds Cloudflare Logs, its SQL API, observability tools and internal analytics. Cathy Chi's post describes five years of work to keep that traffic within hardware budgets. The team's estimate is that the changes together deferred about two and a half years of hardware expansion. Three of them shipped in 2026, and together they removed about 70 GB/s of compressed traffic from the backbone.
The underlying problem repeats every year: traffic grows exponentially while hardware grows roughly linearly. Each saving is a one-time drop on a growth curve, and growth starts eating it immediately.
The pipeline
The pipeline has four stages:
1 runs across the edge fleet. It collects events from sources such as Workers (Cloudflare's serverless platform) and FL (a proxy service that the post doesn't describe).
Logfwdr forwards events over wide-area links to centralized receivers.
The receivers push the events into 2, Cloudflare's distributed buffering system.
Consumers, including the "inserters" that write to 3, read from Buftee and deliver the data to its final destination.
Earlier wins: schema and compression
In 2022 the team broke up a single monolithic message schema. Every product's fields had travelled in one stream, so each internal consumer had to ingest everything and filter out what it didn't need. The team replaced it with per-product schemas. That duplicated some fields, a few of them in every message, yet backbone bandwidth still fell 35–40%. That was roughly a year of growth.
In 2024 the team switched compression from 4 to 5. Earlier experiments with zstd had shown higher CPU use, so they first rolled it out to a few servers and watched the metrics. CPU use showed no noticeable change, so they extended it to the whole fleet. Compressed throughput fell nearly 40%, meaning the same links could carry more than 1.5 times as much traffic. That was worth about ten months of growth. The post doesn't give the zstd level or the CPU figures.
Both of these changes reduced bytes on the wire. The 2026 work went after the uncompressed volume itself.
1. Send once, replicate late
For disaster resilience, non-regional analytics data has to be stored in more than one central region. (Data under 6, a data-residency feature, stays in its designated region.) The old method was double-writing: Logfwdr sent two full copies of every event over wide-area links, one to each region, and each region processed its copy all the way into ClickHouse. Once almost every analytics stream had a secondary region, Logfwdr traffic had doubled.
7, Cloudflare's product for pushing logs to customer destinations, already worked differently. It treats all central receivers as one shared pool. Each message goes to a single region and fails over to another if a machine or site is unreachable. That only works because Logpush is stateless: the customer doesn't care which data center delivered a log. Analytics is different, because both ClickHouse clusters need a full copy so that queries can fail over instantly.
The fix separated getting data to the core from storing it twice:
Shared ingest pool. Logfwdr sends each event once, to whichever receiver is available.
Late replication. Once data lands in Buftee, "remote inserters" stream a copy to the other region's ClickHouse.
Failure handling. If a region goes down, Logfwdr sends all non-regional traffic to the surviving region. If the link between data centers drops briefly, each cluster keeps ingesting its local share without backpressure, and the remote inserters catch up from Buftee's retention buffer afterwards.
The migrated datasets shed about 43 GB/s of transit traffic and added only about 7 GB/s of cross-data-center replication, a net 36 GB/s. The cost of replication is low for two reasons. First, it now happens after the receivers have sampled the data, so the copy is already smaller. Second, data merged in the core is batched uniformly over wider time windows, which compresses far better than small batches from thousands of edge machines.
The first two datasets moved were HTTP analytics, down about 20 GB/s (over 50%), and Ready-Analytics, an internal one-click analytics product. The figures for Ready-Analytics differ within the post: one chart caption says it fell from about 28 to 16 GB/s, while the text says about 26 to 13 GB/s.
2. Sample at the source too
The receivers already sampled data. Now Logfwdr samples as well, before anything crosses the backbone. Each dataset has a sampling key chosen by the team that produces it. For each key, the records are shuffled to avoid bias, and a sample equal to the square root of the record count is kept. Estimates scale back up by the sample rate. In the post's example, keeping 10 of 100 records gives a rate of 1/10, so 10 sampled rows estimate 100 events.
As an illustration (our numbers, not the post's): a key with 10,000 events on one machine keeps 100 of them, while a key with 9 events keeps 3. Busy keys shrink sharply and rare keys keep most of their detail. Applying this twice flattens storage even further. When the team fitted power laws to stored rows against true volume, single sampling grew as x^0.485 and double sampling as x^0.319. A key with 100 times more traffic now stores about 4 times the rows instead of about 10. In one comparison, 11.6 million events were represented by 18,000 rows instead of 79,000, with estimated totals matching the single-sampled baseline.
On three top datasets, traffic to the receivers fell by at least half each, about 25 GB/s or over 2 PB per day in total.
3. Aggregate at every hop
Rollups give exact answers to questions fixed in advance. Examples are unique IPs per minute, or requests per domain per minute grouped by HTTP method. Previously these were computed only in the inserters. Partial aggregates can be merged by key, so the team now aggregates at every stage: in each Logfwdr, again at the receivers (which write to dedicated partitioned buffers), again in the inserters' batches, and finally in ClickHouse's aggregating engines. For the HTTP rollup dataset, which has the highest cardinality, traffic went from over 9 GB/s to under 2 GB/s, about 80% less. A generic product built on this, Ready-Aggregates, lets teams pick aggregation instead of sampling.
Results and caveats
Ready-Analytics shows how quickly growth eats the savings. Load balancing cut it about 50% in mid-May. Two months later it was back at about 20 GB/s, and sampling then brought it down to about 8. The team estimates it would have exceeded 60 GB/s by September without both changes. Its receivers used about 50% less CPU and memory, freeing roughly 750 CPUs and 3.5 TiB of RAM. Across the pipeline, the savings were about 38 GB/s from load balancing, 25 from sampling and 7 or more from aggregation. The team says this halves the data it would otherwise be processing five years from now.
The trade-offs are real. Sampled data gives approximate answers. Rollups require choosing dimensions in advance. With late replication, the second region's copy lags during backbone problems. The post doesn't explain how the two sampling stages' rates combine into the final weights, and it doesn't quantify the CPU cost of pushing work out to the edge.
Remember
Replicating after the data reaches the core cost 7 GB/s, where double-writing from the edge cost 43 GB/s.
Square-root sampling per key shrinks busy keys sharply and leaves rare keys mostly intact.
Partial aggregates can be merged at every hop; one high-cardinality dataset still fell about 80%.
Kent Beck, who created Extreme Programming and popularized 1, has republished a 2013 essay on what he calls 2. These are tests shaped like set up, assert, operation, assert, operation, assert…, borrowing the grammar term for a sentence made of clauses that could each stand alone.
The problem
When a run-on test fails, it doesn't tell you what's wrong. Beck connects this to "specific", one of his 3 (his list of properties good tests should have). If a late assertion fails, the culprit could be the operation right before it. It could also be an earlier one that quietly left the object in a bad state. Either way, you now have a debugging job.
Beck says he writes run-ons himself while exploring a design he doesn't understand yet. Each new discovery gets tacked onto the biggest test that already exists, and he thinks this is why test suites end up with 4 size distributions. Exploring this way is fine, he argues, but leaving "the relics of past ignorance around to trip colleagues" is not.
The recipe
Copy the test and delete everything after the first assertion. Name the copy for what a reader needs to see when it fails: initializationProducesConsistentNamesAndExperiments, not testInitialization. Long names are cheap, because you type a test name once and read it only in failure output.
Copy again. Keep only the set-up, the first operation and the second assertion, and strip any set-up that assertion doesn't need.
Repeat for each remaining assertion, dropping operations that aren't needed. Write down any untested orderings (say, opB(); opA();) and add those tests afterward.
Delete the original.
The result is a set of short tests, each named after a specific failure. That makes assertion messages redundant, and shared set-up can move into a setUp function.
Why coverage holds
The later tests no longer contain the earlier assertions, which feels like losing coverage. Beck argues it isn't, as long as the tests always run together and all stay green: if an earlier behaviour breaks, its own test fails. He calls trusting the whole suite instead of each test the "5" property, and says this point often surprises his students.
The essay is based on experience, with no data. It doesn't address expensive fixtures, end-to-end tests where the sequence is the behaviour under test, or suites that run in separate shards, which weakens the "run them together" condition.
Kent Beck has republished an essay from March 2013 about a common test smell he calls the run-on test. He takes the name from grammar: a run-on sentence joins clauses that could each stand alone. A run-on test joins checks that could each be their own test. A student brought him one shaped like this:
set up
assert
operation
assert
operation
assert
operation
assert
Why 1 hurt
The cost shows up when the test fails. Beck ties it to "specific", one property in his 2 (his list of qualities good tests should have): a failing test should tell you what is wrong. A run-on test doesn't. Say the fourth assertion fails. The cause could be the operation just before it. It could also be any earlier operation that left the object in what Beck calls "a wonky state" without tripping an assertion at the time. So the failure doesn't point at a cause; it hands you a debugging problem.
How they come about
Beck admits he writes run-ons too, mostly while exploring a design he doesn't yet understand. He makes an object, sees it needs an operation, adds it, sees that opens up another, adds that, and each new check lands at the bottom of the same test. In his words, when "every step is a revelation," the obvious place for the next bit of testing is the biggest test that already exists. He suggests this "rich get richer" pattern explains the 3 distributions you see in test suites: a few huge tests and many small ones.
When a design shows up as what he calls "an algebra", meaning a set of things and operations that compose cleanly, he writes small tests from the start and run-ons never appear.
He doesn't object to working in discovery mode; making progress "in a dark room without breaking anything" is a useful skill. His objection is to leaving "the relics of past ignorance around to trip colleagues." The run-on is fine as scaffolding, but it shouldn't stay in the suite.
The conversion recipe
The method is mechanical and keeps coverage the whole way through. You never edit the original until the very end.
Copy the test and cut after the first assertion. You get set up; assert. Give it a name that would help someone reading a failure. Beck's example of a progression: testInitialization is bad (initialization of what? how could it fail?). initializationProducesConsistentExperiments is better. initializationProducesConsistentNamesAndExperiments is best, because it tells you where to look.
Copy again. Delete the first assertion and everything after the second, giving set up; operation; assert. Remove any set-up this assertion doesn't need, and name it the same way.
Repeat for each later assertion. Each step drops preceding operations that turned out to be unnecessary. If you spot paths that aren't covered (Beck's example: "how about opB(); opA();?"), write them down and add those tests after the decomposition, not during it.
Delete the original. Beck calls this the fun part.
To make step 3 concrete, here is a hypothetical example (not from the article). A run-on test for a shopping cart might create a cart, assert it's empty, add an item, assert the count is 1, remove the item, assert it's empty, add two items, and assert the total. Decomposed, that becomes newCartIsEmpty, addingItemIncrementsCount, removingOnlyItemLeavesCartEmpty and totalSumsPricesOfAllItems. The last one may not need the add-and-remove steps at all, so they go.
You end up with short tests, each named after what could go wrong. Beck no longer writes explanation messages in the assertions, since the test name already does that job. Shared set-up can move into a setUp function.
The point that surprises people
The new suite covers exactly what the run-on covered, but only on one condition: you run the tests together and keep all of them passing. Beck says this took him a while to understand and often surprises his students.
Here is the uneasy part. In the original, the final assertion only ran after every earlier assertion had passed. In the split version, the later tests leave those earlier assertions out. It can feel like coverage is lost. Beck's answer is that if an earlier behaviour breaks, the earlier test will fail, so the later test doesn't need to re-check it. This is the "4" property from the Desiderata: you trust the suite as a whole rather than making each test defend itself. That trust is what allows each test to be simple.
What the essay leaves open
The piece is short and argued from experience, without measurements. It doesn't cover:
Expensive set-up such as databases or network fixtures, where repeating set-up in every test has a real runtime cost.
Integration or end-to-end tests, where a sequence of steps may be the very behaviour being tested.
Suites that run in parallel or in pieces, which weakens the "run them together" commitment that the coverage argument depends on.
It also says nothing about how to recognize when exploration is finished. The trigger for decomposing is left to judgment.
The republished post ends with a promotion for Beck's consulting services, which isn't part of the technical argument.
Remember
When a later assertion in a chained test fails, any earlier operation could be the cause.
Name tests for the failure a reader will see; long, explanatory names are cheap and useful.
Splitting keeps coverage only if the tests always run together and all stay passing.
Jim Highsmith co-wrote the 1, the 2001 statement of values behind iterative software development. In an essay on martinfowler.com he names a style of professional writing he calls 2: writing whose authority comes from "having done the work and lived with what happened next." He then sets out guidelines for it. The idea started with feedback. Martin Fowler, reviewing Highsmith's book Wild West to Agile, told him he had written a history when a memoir was trying to get out.
Three ways to disappear
Highsmith argues that the usual registers all remove the writer's judgment. Academic writing hides behind citations and caveats. "Thought leadership" hides behind polish and personal branding. Fluent text generated by language models, which he calls "3," has no author at all and sounds right "until you notice nothing is at stake." What separates real practitioner writing, he says, is accountability: putting your name on a call and living with the result.
What defines it
Authority from consequence. You were responsible for the outcome. Studying it doesn't count.
Start with the claim. The rest of the piece explains it rather than proves it.
Compression as respect. Use fewer definitions and shorter transitions, and trust the reader to fill gaps.
Show the scar. Failure counts as a credential.
Leave tension live. Name the trade-off. Don't wrap it up in three takeaways.
Practical rules
Write the back-cover copy or opening paragraph first. If you can't state the claim in three sentences, you aren't ready to draft. Edit in three separate passes. First, developmental: does the argument work? Second, line: does each paragraph carry one idea? Third, proofreading. Find gaps in evidence early and fix citation formatting late.
Match vocabulary to the reader. A doctor says "idiopathic pulmonary fibrosis" to colleagues and "the lungs are scarring" to the family. With a mixed audience, define a term once and then use it freely.
His before-and-after example: "The contracting model prices the surface and is silent on the substrate…" becomes "The contracting model measures what it can see and misses what it can't. Same blind spot as your dashboard. Just peer-reviewed."
For pieces with several dimensions, he suggests a 4: parallel threads whose meaning comes from where they meet.
Writing with help
Highsmith says Parkinson's disease has slowed his recall and weakened his working memory, and he now drafts with an AI assistant. His test is "is this judgment I earned, or fluency I accepted?" The essay ends with one argument in two forms: Barton Friedland's 1,400-word academic draft and Highsmith's 950-word practitioner rewrite. That rewrite cut the history section, switched to first person and dropped the signposting.
The piece offers no evidence beyond the author's own experience, which is consistent with what it argues for.
The proposal
Jim Highsmith co-wrote the 1, the 2001 statement of values behind iterative software development. In a long essay on martinfowler.com he names a category of professional writing he calls 2 and gives guidelines for doing it. He defines it as writing whose authority comes from "having done the work and lived with what happened next." Credentials, method and audience size are not the source.
The essay began with feedback. Three times in his career, at Cutter Consortium (an IT research firm), at Thoughtworks (a software consultancy), and from Martin Fowler reviewing his book Wild West to Agile, someone told him to "let your voice out." Fowler's note was that Highsmith had written a software history when "a memoir [was] trying to get out." Highsmith reads that repetition as evidence of how strong the pressure to hide is.
The problem: three ways to disappear
Highsmith argues that the two usual models for serious professional writing both remove the author's judgment:
Academic writing hides behind rigor. It uses citations, caveats and literature reviews, puts the main point late, and keeps the author out of view. His complaint is with "rigor [that] becomes camouflage," not with rigor itself.
Thought leadership hides behind polish. In his telling, it went from real ideas, to a content category, to a marketing function, to a LinkedIn post with "a hook, three lessons, and a call to follow for more."
3, fluent text generated by large language models, is the third and newest way to disappear. In his words it is "indistinguishable from the real thing until you notice nothing is at stake."
Whatever style a machine can copy, he says what separates real practitioner writing is accountability: "the willingness to put your name on a call and live with what happens next."
The four distinguishing features
Authority from experience, not credentials. You were responsible for the outcome. You did not just study it. Consultants rarely own outcomes, so for them the authority comes from years spent close to consequences across many organizations: "Earned differently. Still earned."
Tension without resolution. Academic papers resolve tension in their conclusions, and thought leadership resolves it in a list of takeaways. Practitioner Voice names the tension and leaves it open, because "resolving it for them would be lying."
The author stays in. The author's judgment is the content. The data and the frameworks are there to support it.
A narrow readership contract. You write for readers who will recognise the pattern from their own work. Claims are scoped: "this worked here, under these conditions, judge for yourself."
The craft rules
Before drafting: write the back cover first, or for an article the LinkedIn opening. If you can't state the claim in three sentences, the piece isn't ready.
On the page:
Start with the claim. The rest of the piece explains it rather than proves it. A reader who has lived the pattern will recognise it, and Highsmith says no amount of scaffolding will convince one who hasn't.
Compression as respect. Use fewer definitions, shorter transitions and declarative sentences. "Completeness is optional. Resonance is not."
Blend inspiration and information. If a section is purely a story or purely data, work the two back together.
Show the scar. Treat failure as a credential. Don't confine it to a limitations section.
Story and structure. Highsmith uses the Agile Manifesto as his example of an idea that stuck: simple, unexpected, concrete, credible and emotional, plus an origin story (seventeen people at the Snowbird ski resort, two days). The framework comes from Chip and Dan Heath's book Made to Stick. He recommends a 4, where several threads develop in parallel and the meaning comes from where they meet. Wild West to Agile braids five threads across four eras: methods, his personal experience, the pioneers, technology, and management trends. His test for when to braid: you notice you're writing parallel tracks and forcing them into sequence.
Three levels of editing, in order:
5: does the argument and structure work? Flag claims that need support, but don't stop to format citations yet.
Line: does each paragraph carry one clear idea, with good rhythm and word choice?
Proofreading: grammar, typos, links, and final checks on citations.
His rule for citations: "identify gaps early, fix mechanics late." Put sources at the end so they don't interrupt the reader, but always include them. He mentions once finding one of his own book chapters copied word for word online with no attribution.
Word choice. Use a technical term only when it does something a plain word can't. Calibrate to the reader's context. A doctor says "idiopathic pulmonary fibrosis" to a colleague and "the lungs are scarring" to the family. With a mixed audience, define a term once and then use the shorthand. His worked example: "The contracting model prices the surface and is silent on the substrate…" becomes "The contracting model measures what it can see and misses what it can't. Same blind spot as your dashboard. Just peer-reviewed." Sentence-level rules: use active verbs, use fewer -ing words and fewer "that"s, and vary sentence length on purpose. Punctuation sets pace: "A period is a stop sign… A dash is a tree branch in the road."
Writing with machine help
Highsmith says Parkinson's disease has slowed his word retrieval and weakened his working memory. He now drafts with an AI assistant. He frames this with the 6 thesis, a 1998 argument by philosophers Andy Clark and David Chalmers that a tool can be part of a person's thinking, as a notebook is for a man with Alzheimer's. The notebook doesn't direct the man, and Highsmith calls that one-way relationship "everything." His working test is "is this judgment I earned, or fluency I accepted?" Treat generated text as raw material, and never let it settle the voice. He sets this against two other authors: Mik Kersten, who wrote his book without AI, and Jurgen Appelo, who co-wrote with several models. He leaves that disagreement unresolved on purpose.
A side-by-side test
The essay ends with the same argument in two registers. One is an academic-style essay by Barton Friedland, about 1,400 words. The other is Highsmith's practitioner rewrite, about 950 words. He identifies four differences:
He compresses Friedland's three-paragraph history (Plato, McLuhan, Clark and Chalmers) into one paragraph.
He writes in first person where Friedland uses second.
He cuts about a third of the length.
He removes self-signposting such as "So let us name it."
He is open that Friedland's piece is a working draft, and he doesn't argue that one register is better. They reach different audiences. When co-authors write in different registers, he advises dividing the work or braiding the two, rather than blending them into something that loses both.
What's left open
This is an argument from experience with no evidence that readers respond better to it, which fits the style it describes. The "start with the claim, trust the reader" rule has a known cost: readers without the shared experience get little from the piece, and Highsmith accepts that loss openly. For engineering writing such as design docs and postmortems, the editing sequence and the context-calibration rule carry over most directly. The rule against resolving tension sits less comfortably in documents whose job is to reach a decision.
Remember
Separate developmental, line and proofreading passes; polishing sentences before the argument holds wastes effort and can stall a draft.
Define a term once for a mixed audience, then use it freely; jargon left undefined signals group membership but conveys nothing.
Highsmith's practitioner rewrite cut a 1,400-word essay to about 950 words by removing signposts and history.
Generate a fresh database UUID and a fresh DO ID (newUniqueId()). Call init() on the DO from a data center near the original caller, so the database ends up close to the user unless a location hint or jurisdiction is given.
Write the metadata row to the Postgres primary. This is the 5: once the row exists, the database exists, even if the response never reaches the client.
In a fire-and-forget waitUntil(), tell the DO it is active. The DO then writes its own KV entry.