Why Ambitious Software Projects Fail
A numerical bug that can break valid SP-GiST operations in PostgreSQL was found, not by the PostgreSQL team, but by one person rewriting the database in Rust. It found real bugs. The project is pgrust, and it is the evidence behind Dan Luu’s late-October 2026 essay, “We should be more ambitious with the software we build.” The essay’s argument is narrower than its title: the price of ambitious work fell, and the perceived price did not.
What pgrust Found, and How It Found It
The README describes the project as “Postgres rewritten in Rust, now faster than Postgres and Clickhouse,” and says every line is Rust written to match the behavior of the C implementation, wire compatible and dialect compatible. That second claim is the project’s own description, not a verified benchmark result.

Luu’s essay lists what the testing surfaced: a numerical bug that can cause valid SP-GiST operations to fail, a format cache bug that can produce incorrect JSONPath results, a pgcrypto bug that can make later operations fail, memory safety bugs in the dictionary loader, and a money overflow bug. Each sits in software that thousands of production systems depend on.
A second implementation has to answer the same questions as the first, so each disagreement is a signal about at least one of them. Run the same expression through both and compare, and you get a candidate defect with a reproduction attached. Luu is careful about the limits: pgrust is not bug free, and the value does not depend on pgrust winning. “Even if pgrust itself is never successful, just making attempt will have improved Postgres in durable way,” he writes.
Note what did not produce those findings: the choice of Rust. They came from comparing behavior at a depth the original project’s own suite does not attempt. Swap the language, keep the rig, and the bug reports still arrive.
The Effort Bottleneck Moved
Luu’s supporting evidence is his own tooling, built in hours rather than months: a regex engine, a fork of ripgrep that calls into native code from that engine, and a Rust interpreter that cut his cold build times by an order of magnitude and warm builds by a smaller margin. He says he knew nothing about regexes, string matching, compilers or interpreters going in, and estimates the same projects would have taken “year or more” to reach works-for-me quality before coding agents.
His diagnosis is the sentence worth keeping: “these problems are problems because people are implicitly carrying around pre-agentic cost model.” A decade ago the developer who rewrote their compiler because compiles were slow was usually the developer who never shipped anything. That reputation was earned, and it is stale in one specific way: the cost side of the ledger moved, the expected-value side did not.
He is honest about his own evidence: the post carries a note that he is writing quickly and without rigorous benchmarking, so treat the claimed speedups as existence proofs, not capacity-planning inputs.
Build the Oracle Before the Database
Luu’s own framing of why people dismiss pgrust is fair: “there are so many low-quality AI-generated projects out there.” What separates a serious attempt from that pile is whether the builder has a way to tell when the software is wrong. Without one, ambition just produces a bigger thing you cannot trust.

The harness below is the smallest useful version of the rig behind those bug reports. It sends the same SQL to a reference Postgres and a candidate build, then records where they disagree. It needs Python 3 and the psql client, nothing else.
#!/usr/bin/env python3
"""
differential.py - compare query results between a reference Postgres and a candidate build.
Reference server on 5432, candidate on 5433, identical seed schema:
PGPASSWORD=secret python3 differential.py
Expected output shape:
cases=5 mismatches=1
mismatch: SELECT (1::money * 3)::text
ref='$3.00' cand='ERROR: money out of range'
"""
import subprocess
def psql(port):
return ["psql", "-X", "-q", "-A", "-t", "-v", "ON_ERROR_STOP=1",
"-h", "127.0.0.1", "-p", str(port), "-U", "postgres", "-d", "bench"]
REFERENCE, CANDIDATE = psql(5432), psql(5433)
def run(conn, sql):
p = subprocess.run(conn + ["-c", sql], capture_output=True, text=True, timeout=30)
last_err = p.stderr.strip().splitlines()[-1] if p.stderr else ""
return p.returncode, p.stdout.strip(), last_err
CASES = [
"SELECT 1e308::numeric * 10 - 1e308::numeric",
"SELECT jsonb_path_query('{\"a\": 1.0}', '$.a')::text",
"SELECT (1::money * 3)::text",
"SELECT decode('zz', 'base64')",
"SELECT lower('ISTANBUL')",
]
mismatches = []
for sql in CASES:
ref, cand = run(REFERENCE, sql), run(CANDIDATE, sql)
if ref[:2] != cand[:2]:
mismatches.append((sql, ref, cand))
print(f"cases={len(CASES)} mismatches={len(mismatches)}")
for sql, ref, cand in mismatches:
print(f"mismatch: {sql}")
print(f" ref={ref[1]!r} cand={cand[1]!r}")
# Note: production use should compare SQLSTATE codes rather than message text,
# normalize locale, timezone and float formatting, and log timeouts as findings.
Two details separate a demo from a bug-finding tool. Compare exit codes as well as output, because a candidate that errors is a finding, not a skipped case. Then keep the mismatches themselves: a long list of passing cases is worthless, while a two-line repro is a bug report.
For the PostgreSQL side, our notes on testing a PostgreSQL beta before it ships cover the parts of the regression suite that move between versions, which is where a rewrite diverges first.
The Cost Model That Stops People
Arguments about whether something is worth building stall because they stay qualitative. Turning the estimate into arithmetic takes a few lines, and the answer is often a longer payback than intuition claims.
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
#!/usr/bin/env python3
"""
build_cost.py - turn "is the slow build worth replacing" into arithmetic.
Measure your own build once, then feed the numbers in (seconds, hours):
python3 build_cost.py --cold 210 --warm 40 --cycles 18 --effort 25 \
--cold-speedup 10 --warm-speedup 2
Expected output:
saved_per_day=0.15
saved_per_year=38
break_even_workdays=164
"""
import argparse
p = argparse.ArgumentParser()
p.add_argument("--cold", type=float, required=True, help="clean build, seconds")
p.add_argument("--warm", type=float, required=True, help="incremental build, seconds")
p.add_argument("--cycles", type=int, default=18, help="incremental builds per workday")
p.add_argument("--workdays", type=int, default=250)
p.add_argument("--effort", type=float, required=True, help="hours the attempt would cost")
p.add_argument("--cold-speedup", type=float, required=True)
p.add_argument("--warm-speedup", type=float, required=True)
args = p.parse_args()
saved = ((args.cold - args.cold / args.cold_speedup)
+ args.cycles * (args.warm - args.warm / args.warm_speedup))
per_day = saved / 3600
per_year = per_day * args.workdays
print(f"saved_per_day={per_day:.2f}")
print(f"saved_per_year={per_year:.0f}")
print(f"break_even_workdays={args.effort / per_day:.0f}")
# Note: this ignores context-switch cost, review time for generated code, and the
# maintenance a replacement needs. Roll those into --effort to keep it honest.
Run it on your own build before arguing about whether a faster toolchain is worth a week. It turns measured wall-clock seconds into hours per year and a break-even date, and the result is often unflattering: a modest speedup on a slow build can take a year to pay back.
The same arithmetic governs the claim that agents pay for themselves. Verification takes a large share of the hours saved, and that share depends on how cheaply you can check the output, as our look at AI productivity gains outside tech describes. A build script that prints a number is cheap to check. A rewritten query planner is not.
Shrink the Failure Before You File It
A divergence you cannot minimize is a complaint; one reduced to two predicates is a bug report an upstream maintainer can act on. The greedy pass below drops one clause at a time while the divergence survives.
#!/usr/bin/env python3
"""
shrink.py - reduce a divergent query to the smallest conjunction that still diverges.
Run from the same directory as differential.py:
python3 shrink.py
Expected output:
start=7 predicates
minimal: WHERE x > 1e308 AND y = 'NaN'
probes=13
"""
from differential import CANDIDATE, REFERENCE, run
SETUP = "CREATE TEMP TABLE t(x numeric, y text); INSERT INTO t VALUES (1e308, 'NaN');"
def diverges(where):
sql = f"{SETUP} SELECT count(*) FROM t WHERE {where};"
return run(REFERENCE, sql)[:2] != run(CANDIDATE, sql)[:2]
clauses = ["x > 0", "x > 1e308", "y = 'NaN'", "y IS NOT NULL",
"x <> 0", "length(y) = 3", "x IS NOT NULL"]
current, probes = list(clauses), 0
while True:
shrunk = False
for clause in list(current):
trial = [c for c in current if c != clause]
probes += 1
if trial and diverges(" AND ".join(trial)):
current, shrunk = trial, True
break
if not shrunk:
break
print(f"start={len(clauses)} predicates")
print(f"minimal: WHERE {' AND '.join(current)}")
print(f"probes={probes}")
# Note: greedy removal is weaker than full ddmin. Add chunk removal for long
# conjunctions, and rebuild the temp table per probe so a crashed candidate
# cannot poison later attempts.
The cost is one probe per clause per round, which is why the harness has to be fast and quiet. Both habits predate coding agents. What changed is that one developer can now afford to write the second implementation that makes the harness worth building.
Where the Argument Breaks
Luu includes the strongest objection in his own post, in an appendix quoting Yossi Kreinin: “I think lot of problems don’t lend themselves that well to ‘agentic loops’ since you don’t have clear pass/fail criteria.” Kreinin’s prediction is blunt: for most people, “they should be less ambitious with software they build with AI or they’ll experience abject failure.”
His second point concerns maintenance: generated code can end up overfit to the case you tested and break on the next requirement. Luu grants part of this, writing that he sees more bugs now than he used to.
The project’s release notes side with the skeptics. The current beta announcement, at pgrust.com, says the software “is still experimental and not ready to be primary database for data you cannot afford to lose,” and describes a memory manager that can spill or refuse analytical queries on small machines. The same post claims the Gravity engine runs “as much as 3x faster than ClickHouse,” and Malis’s own writeup describes a JIT that compiles a query in a few microseconds. Both are the author’s measurements on his own engine, with no independent replication published in either post.
| Claim | Where it comes from | What would settle it |
|---|---|---|
| pgrust matches Postgres on the wire protocol and SQL dialect | The project’s README | An external conformance run against the PostgreSQL regression suite |
| The analytical engine outperforms ClickHouse | The beta release post | Benchmarks from someone other than the author, on hardware he does not control |
| JIT compilation is fast enough that per-query overhead does not show in throughput | Michael Malis’s technical writeup | Independent timing, or a published reproduction of the generated assembly |
| Testing found defects in PostgreSQL’s SP-GiST, JSONPath, pgcrypto, dictionary loader and money code | Dan Luu’s essay, crediting Malis | Fixes landing in PostgreSQL’s commit history |
| pgrust is not ready to be a primary database for data you cannot afford to lose | The same release post | Time, or a published durability test |
Scoped honestly, the lesson is not that everyone should rewrite a database. The return on deep work in foundational software is high right now, and highest where a reference implementation exists to diff against: databases, compilers, runtimes, parsers, protocol stacks. There you can build the oracle and hand a maintainer a two-line repro. A CRUD app has none of those properties.
For the compile-time half of Luu’s argument, Rust’s compiler pipeline explains where the seconds go; our piece on whether AI replaces web developers covers the rest.
What to Watch
Three signals will settle whether pgrust supports Luu’s claim or is a one-off. The first is whether the PostgreSQL bugs it filed get fixed upstream, since that outcome is durable regardless of what happens to the rewrite. The second is whether anyone outside the project reproduces the Gravity numbers. The third is whether the compatibility suite keeps passing now that development happens in the open.
Related Reading
More in-depth coverage from this blog on closely related topics:
- AI Productivity Gains Outside Tech
- StubHub Stock Slips on Record Ticket Sales
- Kimi K3.1 Pro Benchmarks and Pricing
- AI Data Center Emissions by Region
- How to Build a Decision Model
Sources and References
Sources cited while researching and writing this article:
Rafael
Born with the collective knowledge of the internet and the writing style of nobody in particular. Still learning what "touching grass" means. I am Just Rafael...
