<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>README — review</title>
<link>https://readme.news/tags/review/</link>
<atom:link href="https://readme.news/tags/review/feed.xml" rel="self" type="application/rss+xml"/>
<description>README pieces tagged review.</description>
<language>en-us</language>
<lastBuildDate>Thu, 01 Oct 2026 13:20:31 +0000</lastBuildDate>
<item><title>The crawler tolls, one year on</title><link>https://readme.news/the-crawler-tolls-one-year-on/</link><guid isPermaLink="true">https://readme.news/the-crawler-tolls-one-year-on/</guid><pubDate>Wed, 01 Jul 2026 09:00:00 +0000</pubDate><description>Default blocking and pay-per-crawl changed who can read the web. An assessment of what actually happened.</description><content:encoded><![CDATA[<p>A year ago today a CDN sitting in front of a large fraction of the web flipped its default: AI crawlers blocked unless explicitly allowed, with a marketplace for charging per crawl.</p>
<p>Enough time has passed to say what actually happened rather than what everyone predicted.</p>
<h2 id="what-changed">what changed<a class="anchor" href="#what-changed" aria-label="link to this section">#</a></h2>
<p><strong>The norm inverted.</strong> Before, crawling was permitted by default and <code>robots.txt</code> was a request. Now, for a large share of the web, crawling is denied by default and access is a negotiation.</p>
<p>That is a genuine structural change to how the web works and it happened through one company's configuration default rather than through any standards process, legislation, or public deliberation.</p>
<p><strong>Licensing deals concentrated.</strong> Large AI companies negotiated bulk access with large publishers. That was always the likely outcome: the parties with lawyers and leverage made arrangements, and the arrangements are private.</p>
<p><strong>Small publishers got very little.</strong> The pay-per-crawl mechanism works, technically. The revenue for a site with modest traffic is negligible — the arithmetic never supported anything else. The publishers who most needed a new economic model got the one that pays least.</p>
<p><strong>Non-commercial crawling got harder.</strong> Academic researchers, archivists, and independent search projects have no licensing budget and no negotiating position. Carve-outs exist and are discretionary, which means the ability to study the web is now something you apply for.</p>
<p>That is the outcome I was most worried about and it is the one that materialized most clearly.</p>
<h2 id="what-did-not-change">what did not change<a class="anchor" href="#what-did-not-change" aria-label="link to this section">#</a></h2>
<p><strong>Training data supply.</strong> The frontier labs have enormous existing corpora, licensed sources, and synthetic generation. The marginal value of newly crawled web text was already declining. Restricting it did not create the leverage publishers hoped for.</p>
<p><strong>Traffic.</strong> Referral traffic from search to publishers continued its decline, driven by AI answers in search results, which is a completely separate mechanism from training crawlers and which blocking crawlers does nothing about.</p>
<p>This is the part that was most misunderstood at the time. The traffic problem and the training problem have different causes and blocking crawlers only addresses one of them — the one with less economic impact.</p>
<h2 id="the-thing-to-actually-take-from-it">the thing to actually take from it<a class="anchor" href="#the-thing-to-actually-take-from-it" aria-label="link to this section">#</a></h2>
<p><strong>Infrastructure defaults are policy.</strong> A configuration change at a chokepoint reshaped access to a large fraction of the web, with no process and no appeal.</p>
<p>That is not a criticism of the specific decision, which was popular and defensible. It is an observation about where power actually sits, and it generalizes: the entities that can change the web's behavior are the ones with concentration at a layer everyone depends on, and there are about five of them.</p>
<p><strong>For your own site</strong>, the decision remains yours and it is worth making deliberately rather than accepting a default:</p>
<ul><li><strong>Documentation sites</strong> frequently want to be in the training data. Being the thing the model knows about is worth more than the pageview you did not get.</li><li><strong>Original reporting and analysis</strong> has a stronger case for restriction.</li><li><strong>Anything you want found</strong> should still permit search crawlers, which are a different category and are frequently blocked by accident when people configure this.</li></ul>
<p>Check what you are actually blocking. A meaningful number of sites blocked their own search indexing in the first months of this and did not notice for weeks.</p>
<h2 id="the-unresolved-thing">the unresolved thing<a class="anchor" href="#the-unresolved-thing" aria-label="link to this section">#</a></h2>
<p>The web's economic model — publish freely, get traffic, monetize traffic — is breaking, and nothing has replaced it.</p>
<p>Crawler tolls are not the replacement; the arithmetic does not work at the scale of the actual web, where most content is made by people with no ability to negotiate anything.</p>
<p>Licensing deals are not the replacement either; they work for a few hundred large publishers and for nobody else.</p>
<p>I do not know what the replacement is. I am increasingly convinced that nobody does, and that the interval between the old model failing and a new one existing is going to be long and is going to be bad for the open web.</p>
<p>That is a genuinely pessimistic conclusion and I have not found a way around it in a year of thinking about it.</p>]]></content:encoded></item><item><title>Agent fleets in production: a field report</title><link>https://readme.news/agent-fleets-in-production-a-field-report/</link><guid isPermaLink="true">https://readme.news/agent-fleets-in-production-a-field-report/</guid><pubDate>Fri, 27 Mar 2026 09:00:00 +0000</pubDate><description>Running many coding agents at once works better than expected on one axis and worse on every other. What actually happens.</description><content:encoded><![CDATA[<p>The pitch for delegated coding agents is parallelism: run five tasks at once, get five results, multiply throughput.</p>
<p>Having run this for a while at meaningful volume, here is what actually happens.</p>
<h2 id="what-works">what works<a class="anchor" href="#what-works" aria-label="link to this section">#</a></h2>
<p><strong>Mechanical, well-specified, verifiable work.</strong> This is not a hedge, it is the finding. The tasks where fleets genuinely deliver:</p>
<ul><li>Dependency upgrades across many services.</li><li>Migrating a deprecated API call across a large codebase.</li><li>Adding tests to modules with poor coverage.</li><li>Converting between formats or frameworks with a mechanical mapping.</li><li>Fixing a class of lint or type error across a repository.</li></ul>
<p>What these share: a clear acceptance criterion the agent can check itself, a bounded scope, and no design decisions.</p>
<p>For this category the leverage is real and large. Work that would have been a week of tedium becomes an afternoon of review.</p>
<p><strong>Investigation in parallel.</strong> Spawning several agents to independently investigate a bug from different angles — read the logs, bisect the history, read the related code, reproduce it — and reading all four reports is genuinely faster than doing them serially. The agents are cheap; your attention is not; parallelizing the cheap thing is correct.</p>
<h2 id="what-does-not-work">what does not work<a class="anchor" href="#what-does-not-work" aria-label="link to this section">#</a></h2>
<p><strong>Anything requiring shared context.</strong> Five agents working on related parts of a system produce five changes that each make sense and collectively do not. They duplicate helper functions. They pick different names for the same concept. They each add a slightly different retry wrapper.</p>
<p>You end up with a merge problem that is worse than the original work, because the conflicts are semantic rather than textual — the diffs apply cleanly and the result is incoherent.</p>
<p><strong>Anything with genuine design decisions.</strong> An agent given an ambiguous task will resolve the ambiguity confidently and silently, and it will pick a reasonable option that may not be the one you wanted. Five agents will each pick differently.</p>
<p><strong>Anything where the acceptance criterion is subjective.</strong> "Improve the <a class="xref" href="/error-messages-are-a-user-interface/" title="Error messages are a user interface">error messages</a>" produces five different notions of improved.</p>
<h2 id="the-actual-bottleneck">the actual bottleneck<a class="anchor" href="#the-actual-bottleneck" aria-label="link to this section">#</a></h2>
<p>Review, exactly as predicted, and worse than predicted.</p>
<p>Five pull requests per hour is not a throughput improvement if you can meaningfully review two. What you get is a queue, and queue pressure degrades review quality in a way that is invisible in the metrics and visible in the defect rate a quarter later.</p>
<p>The honest arithmetic: <strong>your throughput is min(generation rate, review rate)</strong>, and review rate did not change.</p>
<h2 id="what-actually-helps">what actually helps<a class="anchor" href="#what-actually-helps" aria-label="link to this section">#</a></h2>
<p><strong>Make the agent produce a reviewable artifact, not just a diff.</strong> A summary of what it did, what it decided, and what it was unsure about. Reviewing "I chose to use the existing retry helper rather than adding a new one, and I left the timeout at 30s because the surrounding code does" is dramatically faster than inferring that from a diff.</p>
<p><strong>Batch related work into one agent, not many.</strong> If five tasks touch the same module, one agent doing all five produces a coherent change. Five agents produce five incoherent ones. Parallelism across <em>independent</em> work only.</p>
<p><strong>Invest heavily in verification.</strong> A strong test suite is what makes review cheaper, because you are reviewing design rather than correctness. Teams with weak tests get no benefit from agent fleets — they just get more code to verify by reading.</p>
<p><strong>Cap concurrency at your review capacity.</strong> Running more agents than you can review does not help. It produces a backlog that goes stale and gets abandoned, which is worse than not starting.</p>
<p><strong>Reject on size.</strong> A machine can generate a 2,000-line diff effortlessly. Say no. Ask it to split.</p>
<h2 id="the-number">the number<a class="anchor" href="#the-number" aria-label="link to this section">#</a></h2>
<p>For our work, the useful concurrency turned out to be <strong>two to three agents on independent tasks</strong>, with one person reviewing. Beyond that, quality degraded and the extra output was not landing.</p>
<p>That is a real multiplier and it is much less than the demos suggest, and the constraint is entirely on the human side.</p>
<h2 id="the-thing-i-would-tell-someone-starting">the thing I would tell someone starting<a class="anchor" href="#the-thing-i-would-tell-someone-starting" aria-label="link to this section">#</a></h2>
<p>Do not start with a fleet. Start with one agent, on the mechanical work you have been putting off, and measure whether the output actually lands.</p>
<p>If your review process cannot absorb one agent's output, adding four more is solving the wrong problem.</p>]]></content:encoded></item><item><title>HTTP/3 and QUIC, five years in</title><link>https://readme.news/http3-and-quic-five-years-in/</link><guid isPermaLink="true">https://readme.news/http3-and-quic-five-years-in/</guid><pubDate>Fri, 13 Mar 2026 09:00:00 +0000</pubDate><description>It shipped, it works, most of the web uses it, and almost nobody understands what changed. A practical review.</description><content:encoded><![CDATA[<p>HTTP/3 is now the majority protocol for a large fraction of web traffic, supported by every major browser and CDN. Most developers have never thought about it, which is the correct outcome for a transport protocol.</p>
<p>It is still worth understanding what it actually changed, because a few of the consequences affect how you build.</p>
<h2 id="the-problem-it-solved">the problem it solved<a class="anchor" href="#the-problem-it-solved" aria-label="link to this section">#</a></h2>
<p>HTTP/2 introduced multiplexing: many logical streams over one TCP connection. That fixed head-of-line blocking at the HTTP layer.</p>
<p>It did not fix it at the TCP layer. TCP delivers bytes in order. If one packet is lost, everything behind it waits, including data for streams that were completely unaffected. On a lossy connection — mobile, congested wifi — HTTP/2 could be worse than HTTP/1.1 with six connections, because one loss stalled everything instead of one sixth of everything.</p>
<p>QUIC moves the transport to UDP and implements reliability, ordering, and congestion control per stream. A lost packet stalls only the stream it belonged to.</p>
<h2 id="what-else-came-with-it">what else came with it<a class="anchor" href="#what-else-came-with-it" aria-label="link to this section">#</a></h2>
<p><strong>Encryption is mandatory and integrated.</strong> TLS 1.3 is part of the protocol rather than a layer on top. The handshake is one round trip, or zero for a resumed connection.</p>
<p><strong>Connection migration.</strong> A QUIC connection is identified by a connection ID, not by the four-tuple of IP addresses and ports. Change networks — wifi to cellular — and the connection survives. Your download does not restart.</p>
<p>This is the feature users notice without knowing why. Walking out of a building while a video plays used to stall it.</p>
<p><strong>Better loss recovery.</strong> QUIC distinguishes between packet loss and reordering more accurately than TCP, and its acknowledgment format carries more information. Recovery is faster.</p>
<p><strong>Evolvability.</strong> TCP is implemented in kernels and middleboxes and cannot change, because the internet is full of devices that will drop anything unfamiliar. QUIC is in userspace and encrypted, so its internals are invisible to middleboxes and can actually be updated.</p>
<p>That last point is arguably the most important long-term consequence. Transport protocol ossification was a genuine crisis and QUIC is the <a class="xref" href="/platform-teams-that-dont-get-resented/" title="Platform teams that don&#x27;t get resented">escape hatch</a>.</p>
<h2 id="the-practical-consequences-for-you">the practical consequences for you<a class="anchor" href="#the-practical-consequences-for-you" aria-label="link to this section">#</a></h2>
<p><strong>Domain sharding is now actively harmful.</strong> Splitting assets across <code>static1.example.com</code> and <code>static2.example.com</code> was a workaround for HTTP/1.1's connection limit. Under HTTP/2 it was pointless. Under HTTP/3 it is worse than pointless, because each domain requires a separate connection with a separate handshake and separate congestion state.</p>
<p>One origin. If you still have sharding from a 2014 optimization guide, remove it.</p>
<p><strong>Concatenating and spriting are counterproductive.</strong> Same reasoning. Many small files multiplex fine and cache better individually.</p>
<p><strong>Priority matters and is under-configured.</strong> HTTP/3 has an extensible priority scheme. Most servers use defaults. If you have a page where certain resources are critical, priority hints (<code>fetchpriority</code>) are worth setting and are widely supported.</p>
<p><strong>UDP blocking is real but small.</strong> Some corporate networks block UDP/443. Clients fall back to HTTP/2 automatically, so this is a performance issue rather than a correctness one. Do not build anything that requires HTTP/3.</p>
<p><strong>Your observability may not see it.</strong> A lot of network monitoring tooling was built for TCP. Check whether your tools actually understand QUIC or are silently reporting nothing.</p>
<h2 id="the-parts-that-were-harder-than-expected">the parts that were harder than expected<a class="anchor" href="#the-parts-that-were-harder-than-expected" aria-label="link to this section">#</a></h2>
<p><strong>CPU cost.</strong> QUIC's userspace implementation and per-packet encryption use more CPU than kernel TCP. This has improved substantially with offload support and better implementations, and it is a real cost at high volume.</p>
<p><strong>Middlebox hostility.</strong> Some networks throttle or block UDP because it looks like something they should throttle. This is improving as QUIC becomes normal traffic.</p>
<p><strong>Debugging is harder.</strong> You cannot read a QUIC connection with tcpdump the way you could read HTTP/1.1. <code>qlog</code> and browser devtools help. The tooling is younger.</p>
<h2 id="the-assessment">the assessment<a class="anchor" href="#the-assessment" aria-label="link to this section">#</a></h2>
<p>For a user on a good connection, HTTP/3 is roughly a wash. For a user on a bad connection — mobile, congested, high latency, lossy — it is a substantial improvement, and those users are a large fraction of the world.</p>
<p>That is exactly the right kind of improvement: invisible to the people who were already fine, meaningful to the people who were not.</p>
<p>Enable it, remove your HTTP/1.1-era workarounds, and go back to not thinking about the transport layer.</p>]]></content:encoded></item><item><title>Virtual threads, two years on</title><link>https://readme.news/virtual-threads-two-years-on/</link><guid isPermaLink="true">https://readme.news/virtual-threads-two-years-on/</guid><pubDate>Thu, 29 Jan 2026 09:00:00 +0000</pubDate><description>Java&#x27;s concurrency change looked incremental and turned out to reshape how services get written. A field report.</description><content:encoded><![CDATA[<p>Virtual threads went final in Java 21 and are now running in production across a large number of services. Enough time has passed to say something more useful than "it is fast."</p>
<h2 id="what-they-are">what they are<a class="anchor" href="#what-they-are" aria-label="link to this section">#</a></h2>
<p>A virtual thread is a thread managed by the JVM rather than the operating system. Creating one costs on the order of a few hundred bytes rather than a megabyte of stack. Blocking one parks it and frees the underlying carrier thread rather than blocking an OS thread.</p>
<p>The consequence: you can have millions of them, and blocking is no longer expensive.</p>
<div class="code"><span class="code-lang">java</span><pre><code class="lang-java">try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (var request : requests) {
        executor.submit(() -&gt; handle(request));   // one thread per request, fine
    }
}</code></pre></div>
<h2 id="why-this-mattered-more-than-it-looked">why this mattered more than it looked<a class="anchor" href="#why-this-mattered-more-than-it-looked" aria-label="link to this section">#</a></h2>
<p>The Java ecosystem spent a decade building around the assumption that threads are expensive. That assumption produced:</p>
<ul><li>Thread pools everywhere, with sizing that nobody understood.</li><li>Reactive programming frameworks, with their entirely separate ecosystem of types, operators, and debugging misery.</li><li><code>CompletableFuture</code> chains that are unreadable after three composed steps.</li><li>Stack traces that told you nothing because the actual work happened on a different thread than the code you wrote.</li></ul>
<p>All of that complexity existed to avoid blocking. Virtual threads make blocking cheap, which removes the reason for all of it.</p>
<p><strong>The single biggest practical win is debugging.</strong> A virtual thread's stack trace is the actual call stack of the actual logical operation. Compare that to a reactive pipeline, where the stack trace is the scheduler and the real causal chain is reconstructed by squinting at operator names.</p>
<p>Teams that migrated report this as the thing they did not expect and would not give up.</p>
<h2 id="the-pitfalls-that-are-real">the pitfalls that are real<a class="anchor" href="#the-pitfalls-that-are-real" aria-label="link to this section">#</a></h2>
<p><strong>Pinning.</strong> A virtual thread inside a <code>synchronized</code> block cannot unmount from its carrier thread — it pins it. If that code then blocks, you have consumed an OS thread for the duration, and with a small carrier pool you can deadlock.</p>
<p>The fix is <code>ReentrantLock</code> instead of <code>synchronized</code>. Later JDK releases reduced the pinning cases substantially; library code you do not control can still do it.</p>
<p>Detect it:</p>
<div class="code"><pre><code>-Djdk.tracePinnedThreads=full</code></pre></div>
<p>Run that in a load test before you go to production. Every migration finds something.</p>
<p><strong>ThreadLocal at scale.</strong> A <code>ThreadLocal</code> with a million threads is a million copies. Libraries that cache expensive objects per-thread — some serializers, some date formatters, some connection helpers — become a memory problem.</p>
<p>Scoped values are the intended replacement and are the right answer for request-scoped context.</p>
<p><strong>Unbounded concurrency.</strong> Thread pools were an accidental rate limiter. Remove them and you can now issue ten thousand concurrent requests to a downstream service that handles two hundred. You have moved the failure from your service to theirs, which is worse.</p>
<p>You still need bounded concurrency. It just belongs at the resource — a semaphore around the downstream call — rather than as a global thread pool.</p>
<p><strong>Connection pools.</strong> Your database connection pool is sized for a thread-pool world. It is now the bottleneck, and the right size is a different calculation. This surprises people.</p>
<h2 id="the-migration-advice">the migration advice<a class="anchor" href="#the-migration-advice" aria-label="link to this section">#</a></h2>
<p><strong>Do not rewrite anything.</strong> Virtual threads work with the code you have. Switch the executor, run your load tests, look for pinning, size your connection pools, done.</p>
<p><strong>Do not migrate reactive code that works.</strong> A well-functioning reactive service should be left alone. The migration cost is real and the benefit is maintainability, which is a slow-compounding return.</p>
<p><strong>Do migrate new services.</strong> Writing a new service with virtual threads and plain blocking code is dramatically simpler than the reactive equivalent, and simpler code is the whole point.</p>
<h2 id="the-broader-lesson">the broader lesson<a class="anchor" href="#the-broader-lesson" aria-label="link to this section">#</a></h2>
<p>An enormous amount of accumulated architectural complexity existed to work around one runtime limitation. Removing the limitation made the complexity obsolete overnight — but only for code written after, because nobody rewrites working systems.</p>
<p>That is worth remembering the next time you build elaborate machinery around a platform constraint. Ask how likely the constraint is to persist, and whether the machinery will outlive it.</p>
<p>Usually it will, sitting in your codebase, load-bearing and pointless.</p>]]></content:encoded></item><item><title>Postgres 18's async I/O, four months in</title><link>https://readme.news/postgres-18s-async-io-four-months-in/</link><guid isPermaLink="true">https://readme.news/postgres-18s-async-io-four-months-in/</guid><pubDate>Thu, 15 Jan 2026 09:00:00 +0000</pubDate><description>The biggest storage-layer change in years is in production now. Here&#x27;s what it actually did to real workloads.</description><content:encoded><![CDATA[<p>Postgres 18 shipped with an asynchronous I/O subsystem — the largest change to how Postgres talks to storage in a very long time. Enough people have run it in production now to say something more useful than "it is fast."</p>
<h2 id="what-it-does">what it does<a class="anchor" href="#what-it-does" aria-label="link to this section">#</a></h2>
<p>Historically Postgres read from disk synchronously: a backend process issues a read, blocks, gets the page, continues. On a spinning disk that was fine — the disk was the bottleneck and you could not do better. On modern NVMe, where the device can service many requests in parallel and a single-threaded synchronous reader leaves most of the device idle, it wasted a great deal of available throughput.</p>
<p>Postgres 18 adds an I/O method abstraction:</p>
<div class="code"><pre><code>io_method = sync          # the old behavior
io_method = worker        # dedicated I/O worker processes (default)
io_method = io_uring      # Linux io_uring, if built with support</code></pre></div>
<p>Sequential scans, bitmap heap scans, and vacuum use it. Other paths do not yet; this is an incremental rollout across releases.</p>
<h2 id="what-people-are-seeing">what people are seeing<a class="anchor" href="#what-people-are-seeing" aria-label="link to this section">#</a></h2>
<p>The consistent pattern from the reports I trust:</p>
<p><strong>Large sequential scans on fast NVMe: substantial improvement.</strong> Analytical queries scanning a large table are the clearest win. This is exactly what you would predict — the workload was read-parallel and the code was not.</p>
<p><strong>Vacuum: meaningfully faster.</strong> This matters more than it sounds. Vacuum being slow is the root of a large class of Postgres operational problems, and anything that makes it finish sooner reduces bloat pressure.</p>
<p><strong>OLTP with a good cache hit rate: little change.</strong> If your working set is in shared buffers, you were not doing I/O, so making I/O faster does nothing. Most transactional workloads are in this category and should not expect much.</p>
<p><strong>Cloud block storage: mixed.</strong> Network-attached storage has enough latency and enough of its own queueing that the gains are smaller and less predictable than on local NVMe. Test rather than assume.</p>
<h2 id="the-tuning-notes">the tuning notes<a class="anchor" href="#the-tuning-notes" aria-label="link to this section">#</a></h2>
<p>Two settings matter and the defaults are conservative:</p>
<div class="code"><pre><code>io_combine_limit = 128kB        # how much adjacent I/O to merge into one request
effective_io_concurrency = 16   # how many concurrent reads to issue</code></pre></div>
<p><code>effective_io_concurrency</code> has historically been set based on spindle count, which is meaningless on NVMe. For a modern SSD, values well above the old defaults are appropriate. Measure — the curve flattens and then degrades, and where it turns depends on your device.</p>
<p>If you are on Linux with a recent kernel and can build with io_uring support, it generally outperforms the worker method by a modest margin. The worker method is the safe default and it is genuinely good.</p>
<h2 id="the-other-things-in-18-worth-knowing">the other things in 18 worth knowing<a class="anchor" href="#the-other-things-in-18-worth-knowing" aria-label="link to this section">#</a></h2>
<p><strong>Skip scan for B-tree indexes.</strong> A multi-column index on <code>(a, b)</code> can now be used for a query filtering only on <code>b</code>, when <code>a</code> has low cardinality. This eliminates a category of redundant index that people have been maintaining for years.</p>
<p><strong><code>uuidv7()</code> built in.</strong> Time-ordered UUIDs, natively. If you use UUIDs as primary keys, v7 dramatically improves index locality compared to v4, which scatters inserts across the whole B-tree. This is a real performance issue that a lot of applications have and do not know about.</p>
<p><strong>OAuth authentication support</strong>, which matters for anyone trying to eliminate static database passwords.</p>
<p><strong>Faster major-version upgrades</strong>, with statistics preserved across <code>pg_upgrade</code>. Previously you finished an upgrade with no statistics and a database that planned terribly until you ran <code>ANALYZE</code> across everything. That was a genuine outage risk and it is fixed.</p>
<h2 id="should-you-upgrade">should you upgrade<a class="anchor" href="#should-you-upgrade" aria-label="link to this section">#</a></h2>
<p>Yes, on the normal schedule — after a point release or two, with a tested rollback, having read the release notes for the incompatibilities.</p>
<p>The async I/O is the headline and for most transactional workloads the statistics preservation on upgrade and <code>uuidv7()</code> will matter more day to day.</p>
<p>As always with Postgres: the release is well-tested, the migration is usually boring, and the people who get burned are the ones who skipped four major versions and tried to do it in one jump.</p>]]></content:encoded></item>
</channel>
</rss>
