10 interesting stories served every morning and every evening.

FTC secures right to repair settlement with farming equipment giant John Deere | AP News

apnews.com

It looks like John Deere own­ers can soon feel free to fix their own ma­chines.

The Federal Trade Commission and at­tor­neys gen­eral from sev­eral states se­cured a right-to-re­pair set­tle­ment Wednesday with agri­cul­ture equip­ment gi­ant Deere & Co. — com­monly known as John Deere — that re­quires the com­pany to let farm­ers and in­de­pen­dent shops fix their own equip­ment.

The Illinois-based man­u­fac­turer has faced com­plaints for years for with­hold­ing the soft­ware needed for re­pairs and forc­ing cus­tomers to use au­tho­rized deal­ers in­stead of in­de­pen­dent ones.

This marks the sec­ond right-to-re­pair set­tle­ment Deere has reached this year, fol­low­ing a sep­a­rate $99 mil­lion class-ac­tion set­tle­ment with farm­ers in April. Though the class-ac­tion com­pen­sated con­sumers, the FTCs set­tle­ment in­stead re­quires Deere to make its re­pair ser­vices avail­able to equip­ment own­ers and in­de­pen­dent shops.

The FTC and at­tor­neys gen­eral from Arizona, Illinois, Michigan, Minnesota and Wisconsin brought the an­titrust law­suit in January 2025, ar­gu­ing that Deere had il­le­gally re­stricted farm­ers and in­de­pen­dent shops that might oth­er­wise ser­vice them from re­pair­ing farm equip­ment such as trac­tors. Deere also makes en­gines and equip­ment for forestry, land­scap­ing and con­struc­tion.

Under the or­der filed in Illinois, Deere will now be re­quired to make di­ag­nos­tic and re­pair tools avail­able to equip­ment own­ers and in­de­pen­dent re­pair shops, not only its own net­work of au­tho­rized deal­ers. It also pre­vents Deere deal­ers from re­tal­i­at­ing against equip­ment own­ers or re­pair shops who choose to fix their own equip­ment in­stead of pay­ing for Deere’s ser­vices. The or­der is headed to Judge Iain D. Johnston for his ap­proval.

For too long, Arizona farm­ers and in­de­pen­dent me­chan­ics have been at the mercy of Deere’s mo­nop­oly over re­pair tools, forced to wait — and pay — for au­tho­rized deal­ers just to fix bro­ken trac­tors and other equip­ment,” Arizona Attorney General Kris Mayes said in a state­ment Wednesday.

Deere must pay $1 mil­lion col­lec­tively to the five states for an­titrust en­force­ment costs and will be sub­ject to strict com­pli­ance over­sight for the next 10 years.

In the com­plaint, the FTC ar­gued that Deere pro­vides a ser­vice soft­ware tool to au­tho­rized deal­ers but does not pro­vide the full ver­sion to equip­ment own­ers or in­de­pen­dent shops. Deere had said the law­suit was base­less, de­nied that its dis­tri­b­u­tion of ser­vice tools was an­ti­com­pet­i­tive and ar­gued that it could not mo­nop­o­lize ser­vices since it does not di­rectly pro­vide them.

Sign up for Morning Wire: Our flag­ship newslet­ter breaks down the biggest head­lines of the day.

Deere main­tained its com­mit­ment to in­de­pen­dent re­pair in a state­ment Wednesday, adding that the agree­ment with the FTC re­in­forces its in­no­va­tion of more flex­i­ble re­pair op­tions.

This is good news for our cus­tomers and for the fu­ture of how Deere equip­ment is sup­ported,” said Denver Caldwell, vice pres­i­dent of af­ter­mar­ket and cus­tomer sup­port.

Right-to-repair has be­come an in­creas­ingly com­mon is­sue over the years, es­pe­cially for tech prod­ucts, with con­sumers com­plain­ing that even sim­ple re­pairs can only be done by com­pany-au­tho­rized deal­ers.

Attention Required! | Cloudflare

x.ai

Why have I been blocked?

This web­site is us­ing a se­cu­rity ser­vice to pro­tect it­self from on­line at­tacks. The ac­tion you just per­formed trig­gered the se­cu­rity so­lu­tion. There are sev­eral ac­tions that could trig­ger this block in­clud­ing sub­mit­ting a cer­tain word or phrase, a SQL com­mand or mal­formed data.

What can I do to re­solve this?

You can email the site owner to let them know you were blocked. Please in­clude what you were do­ing when this page came up and the Cloudflare Ray ID found at the bot­tom of this page.

openai.com

Rewriting Bun in Rust

bun.com

Disclosure: Bun was ac­quired by Anthropic in December 2025. I and oth­ers on the Bun team work at Anthropic. I used a pre-re­lease ver­sion of Claude Fable 5 for much of the Rust rewrite.

Bun started as a line-for-line port of es­build’s JavaScript & TypeScript tran­spiler from Go to Zig. I wrote my first line of Zig on April 16, 2021. I bet on Zig af­ter see­ing the sin­gle-page Zig Language Reference on Hacker News and get­ting re­ally ex­cited about the low-level con­trol and care for per­for­mance.

From the start, Bun’s scope was mas­sive:

JavaScript, TypeScript, and CSS tran­spiler, mini­fier, and bundler

npm-com­pat­i­ble pack­age man­ager

Jest-like test run­ner

Node.js & TypeScript-compatible mod­ule res­o­lu­tion

HTTP/1.1 & WebSocket client

Node.js API im­ple­men­ta­tions like fs, net, tls, and dozens of other mod­ules

The ini­tial ver­sion of Bun was writ­ten by me in 1 year, in a cramped Oakland apart­ment, pre-LLM, in Zig. The de­fault out­come for am­bi­tiously-scoped pro­jects like Bun is join­ing the grave­yard of dead side pro­jects on a GitHub pro­file page. Zig made Bun pos­si­ble. I would never have been able to build this much in 1 year if it was­n’t for Zig.

Nowadays, Bun’s CLI gets over 22 mil­lion monthly down­loads. Popular tools like Claude Code and OpenCode bet on Bun as their run­time. Vercel, Railway, DigitalOcean and more have 1st-party sup­port for Bun.

Bun’s scope has also been a chal­lenge for sta­bil­ity. Here’s a small sam­ple of bugs we fixed in Bun v1.3.14:

heap-use-af­ter-free crash in node:zlib when call­ing .reset() on a zlib, Brotli, or Zstd stream while an async .write() is still in progress on the thread­pool

use-af­ter-free crash in node:zlib when an on­error call­back is­sued a re-en­trant write() fol­lowed by close() on na­tive han­dles

use-af­ter-free crashes in node:http2 when re-en­trant JS call­backs (e.g. ses­sion.re­quest() in­side a time­out lis­tener, an op­tions get­ter, or a write call­back) trig­gered a hashmap re­hash, in­val­i­dat­ing in­ter­nal stream point­ers

use-af­ter-free in UDPSocket.send() and send­Many() where user code in val­ueOf() or toString() call­backs could de­tach an ArrayBuffer be­tween pay­load cap­ture and the ac­tual send

crash and out-of-bounds read in Buffer#copy and Buffer#fill when a val­ueOf call­back de­taches or re­sizes the un­der­ly­ing ArrayBuffer dur­ing ar­gu­ment co­er­cion

heap out-of-bounds write in UDPSocket.sendMany() when the sock­et’s con­nec­tion state changed mid-it­er­a­tion via user JS call­backs

mem­ory leak in crypto.scrypt where the call­back and pro­tected pass­word/​salt buffers were never re­leased when the out­put buffer al­lo­ca­tion failed

SSLWrapper.init leaked the strdup’d passphrase on er­ror paths

mem­ory leak in tlsSocket.set­Ses­sion() where each call leaked one SSL_SESSION (~6.5 KB per call) due to a miss­ing SSL_SESSION_free af­ter d2i_SS­L_SES­SION

mem­ory leak where fs.watch() watch­ers were never garbage col­lected af­ter .close(), caused by a ref­er­ence count un­der­flow that per­ma­nently pinned each watcher as a GC root

dou­ble-free crash in the CSS parser when back­ground-clip had ven­dor pre­fixes and multi-layer back­grounds

DuplexUpgradeContext was never freed — a full leak per tls.con­nect({ socket: du­plex })

race con­di­tion crash in MessageEvent where the GC marker thread could ob­serve a torn vari­ant in m_­data dur­ing con­cur­rent ac­cess from a BroadcastChannel or MessagePort

We could have kept fix­ing these kinds of bugs one-off in per­pe­tu­ity, but we owe it to our users count­ing on us to do bet­ter than that, and sys­tem­at­i­cally pre­vent these kinds of bugs from re­cur­ring.

What we were al­ready do­ing

We patched the Zig com­piler to add Address Sanitizer sup­port. We run our test suite with ASAN on every com­mit.

We ship Zig safety-checked ReleaseSafe builds on Windows

We fuzz Bun’s run­time APIs 24/7 us­ing Fuzzilli, the JavaScript en­gine fuzzer used by V8 & JavaScriptCore

We have a whole lot of end-to-end mem­ory leak tests

This is more than many pro­jects do.

Just be re­ally smart and don’t make mis­takes?

Our bug­fix list felt bad and I was tired of go­ing to sleep wor­ry­ing about crashes in Bun. I don’t blame Zig for that - other users of Zig don’t have the bugs we had, and mix­ing GC with man­u­ally-man­aged mem­ory is an un­com­mon enough thing for soft­ware to need that no lan­guage re­ally de­signs for it. We would­n’t have got­ten this far if not for Zig, and I’ll al­ways be grate­ful. Until very re­cently, pro­gram­ming lan­guage choice was a one-way de­ci­sion for a pro­ject like Bun.

JavaScript is a garbage-col­lected lan­guage and mod­ern JavaScript en­gines like JavaScriptCore (and V8) have strict rules around ex­cep­tion han­dling and the garbage col­lec­tor. Zig, like C, does­n’t man­age mem­ory for you and this is a trade­off that for many pro­jects is a great rea­son to use Zig. Zig does not have con­struc­tors/​de­struc­tors, and most cleanup is ex­pected to be writ­ten out ex­plic­itly at each call site with de­fer.

For Bun, cor­rectly han­dling the life­times of garbage-col­lected val­ues and man­u­ally-man­aged val­ues has been a ma­jor source of sta­bil­ity is­sues - most of­ten small mem­ory leaks and oc­ca­sion­ally, crashes. Every mem­ory al­lo­ca­tion has to be metic­u­lously re­viewed. Where do these bytes get freed? How do we en­sure it only gets freed once? Did we check for JavaScript ex­cep­tions prop­erly? Is this garbage-col­lected pointer vis­i­ble to the con­ser­v­a­tive stack scan­ner? Is this garbage col­lected mem­ory or man­u­ally man­aged mem­ory?

For sta­bil­ity is­sues, know­ing as early as pos­si­ble is best. Fuzzing hap­pens af­ter code is merged. CI hap­pens when code is pushed. Runtime safety checks & ad­dress san­i­tizer hap­pens when code is run (hopefully in de­vel­op­ment, be­fore CI).

One com­mon way to re­duce this class of is­sue is to en­sure cleanup code is al­ways run ex­actly once for code that needs it. Zig is de­signed to be a sim­ple lan­guage with no hid­den con­trol flow, and so it prefers the ex­plicit de­fer key­word to run code at the end of a scope over C++’s im­plicit ~Destructor or Rust’s im­plicit Drop.

For Zig code, when ex­actly should we be run­ning the cleanup code? If we’re pass­ing the same *T to many dif­fer­ent func­tions, how do we know when it’s no longer ac­ces­si­ble and can be cleaned up? How does it work when some func­tions need to con­tinue to ref­er­ence the mem­ory af­ter the func­tion is called? Our cur­rent ap­proach is a mix of:

arena life­times, where the scope of when it’s ac­ces­si­ble is clear (parser state does­n’t es­cape the call­ing func­tion and so AST nodes are a good choice there)

ref­er­ence-count­ing

pay re­ally close at­ten­tion

Many pro­jects opt to an­swer these kinds of ques­tions through a style guide. TigerBeetle’s TigerStyle is an ex­am­ple in Zig and Google’s 31,000 word C++ style guide is an­other. The chal­lenge with style guides is en­force­ment. How do you make sure the style guide is fol­lowed? Historically, code re­view was the an­swer with best-ef­fort en­force­ment via lin­ters & sta­tic an­a­lyz­ers.

Having a rigid style guide with clear own­er­ship ex­pec­ta­tions ex­plic­itly spelled out in the type sys­tem was a real op­tion for Bun. Since Zig has no op­er­a­tor over­load­ing, we would likely end up with a lot of code look­ing some­thing like this:

fn foo(a_ptr: SharedPtr(TCPSocket)) !void { const a: *TCPSocket = a_ptr.get(); de­fer a_ptr.deref();

const b = try do_­some­thing_with_a(a); de­fer b.deref();

// … }

This is less er­gonomic than the Zig we ex­pect:

fn foo(a: *TCPSocket) !void { const b = try do_­some­thing_with_a(a); // … }

What about C/C++?

About 20% of Bun’s code is writ­ten in C++ and Bun em­beds sev­eral C/C++ li­braries:

JavaScriptCore, the JavaScript en­gine that pow­ers Safari

uWeb­Sock­ets & usock­ets - our HTTP/WebSocket server, and event loop

lsh­pack & lsquic - HPACK and HTTP/3 li­braries

BoringSSL, Google’s OpenSSL fork

SQLite

C++ in­stead of Zig would be a rea­son­able choice for Bun. We would get con­struc­tors & de­struc­tors. We could delete lots of ex­tern C” wrap­per code.

But, we would still be re­liant on style guides en­forced through code re­view, and even with ASAN, mem­ory cor­rup­tion and mem­ory leaks would still hap­pen.

Why Rust?

A large per­cent­age of bugs from that list are use-af­ter-free, dou­ble-free, and forgot to free” in an er­ror path. In safe Rust, these are com­piler er­rors and RAII-like au­to­matic cleanup with Drop. Compiler er­rors are a bet­ter feed­back loop than a style guide.

Historically, rewrites are a ter­ri­ble idea. Excluding com­ments, Bun is 535,496 lines of Zig. A rewrite in an­other lan­guage would take a small team of en­gi­neers a full year. It would mean freez­ing bug­fixes, se­cu­rity fixes or fea­ture de­vel­op­ment for that time. The least risky ap­proach to get­ting some­thing ship­pable would be a me­chan­i­cal port from Zig to Rust, with the min­i­mal num­ber of be­hav­ioral changes, us­ing the ex­act same test suite we al­ready use for test­ing Bun.

Fortunately, Bun’s own test suite is writ­ten in TypeScript which means it does­n’t de­pend on the run­time’s pro­gram­ming lan­guage.

A year of zero user-fac­ing im­pact is not a re­al­is­tic op­tion we could con­sider. So, en­force­ment through code-style to fix sta­bil­ity is­sues was our best bet, and was our plan when we added Rust-inspired smart point­ers to Bun’s code­base.

But hon­estly, I did­n’t want to do it. Homegrown smart point­ers of­fer worse er­gonom­ics than Rust, with none of the guar­an­tees.

What if, in­stead, I spend a week test­ing if Anthropic’s new model can rewrite Bun in Rust?

At first, I did­n’t ex­pect it to work. A few days in, a high % of the test suite started pass­ing and I saw how much the new Rust code matched up with the orig­i­nal Zig code­base. My opin­ion went from this is worth try­ing” to I’m go­ing to merge this”.

Claude, rewrite Bun in Rust.

There are a lot of ways to do a ter­ri­ble job of this. For ex­am­ple, prompt­ing Claude Rewrite Bun in Rust. Don’t make any mis­takes.” and then pray­ing it would work is not what I did.

Think about how a per­son would do this. The first big ques­tion is:

Incremental rewrite? Or, every­thing all at once?

In my ex­pe­ri­ence port­ing es­build’s tran­spiler from Go to Zig for the ini­tial ver­sion of Bun (without LLMs), every­thing all at once is bet­ter. An in­cre­men­tal rewrite adds tem­po­rary code that you hope gets deleted even­tu­ally, and would be painful in the short-medium term.

The sec­ond big ques­tion: how?

How do we keep Bun in Rust the same Bun as be­fore, with the same ar­chi­tec­ture, per­for­mance, and fea­ture-set while also get­ting the lan­guage fea­tures of Rust like the bor­row checker? How do we en­sure the team can still main­tain it af­ter the rewrite?

Do the rewrite that looks like we tran­spiled our Zig code to Rust. We can grad­u­ally refac­tor it to re­duce un­safe us­age and look more like id­iomatic Rust af­ter Bun v1.4 ships.

Those are the only two big ques­tions. Everything else is tac­tics.

Loops that write & re­view code

A lot of day-to-day en­gi­neer­ing work as soft­ware en­gi­neers can be over-sim­pli­fied into loops.

// Pseudocode, not real code: let task; while ((task = todoList.pop())) { const re­sult = task(); const feed­back = await Promise.all([review(result), re­view(re­sult)]); await ap­ply(feed­back, re­sult); }

A task has some con­text as­so­ci­ated with it (a Jira ticket, a GitHub is­sue, etc). The re­sult is the code you wrote to fix it. Code re­viewer(s) re­view the changes to check for re­gres­sions & cor­rect­ness. And then you ad­dress the feed­back.

I rewrote Bun in Rust us­ing about 50 dy­namic work­flows in Claude Code run con­tin­u­ously over the course of 11 days.

Each dy­namic work­flow was a loop like this - a work­flow for:

Generate a port­ing guide map­ping Zig pat­terns & types to Rust pat­terns & types

Mechanically port every .zig file to a .rs file, match­ing the PORTING.md and LIFETIMES.tsv

Fix every crate’s com­piler er­rors

Get sub­com­mands like bun test or bun build to work

Get every test in Bun’s en­tire test suite to pass

Several large refac­tors and cleanup passes

For most of those 11 days (and af­ter), I mon­i­tored work­flows - man­u­ally read­ing the out­puts to check for is­sues and bugs, and prompt­ing Claude to edit the loop to fix things.

How do you re­view a PR with +1 mil­lion lines added? How do you start to build the con­fi­dence needed to re­spon­si­bly merge large quan­ti­ties of LLM-authored code?

A lan­guage-in­de­pen­dent test suite with a mil­lion as­ser­tions, ad­ver­sar­ial code re­view and when some­thing does go wrong, fix­ing the process that gen­er­ates the code in­stead of hand-fix­ing the code.

Adversarial re­view

Adversarial re­view asks Claude (in a sep­a­rate con­text win­dow) to ex­haus­tively come up with rea­sons why the changes cre­ate bugs or do not work.

Split con­text win­dows

Usually with hu­mans, the per­son re­view­ing the code is not the per­son who au­thored the code. The per­son writ­ing the code wants to merge the code, which can bias their ac­tions to ship be­fore it’s ready.

Claude is the same way. The Claude that wrote the code wants the code to get ac­cepted. The Claude that re­views wants to find is­sues in the code.

1 im­ple­menter, 2 or more ad­ver­sar­ial re­view­ers per im­ple­menter. The re­view­er’s only job: find bugs & rea­sons why the code does not work. The im­ple­menter does­n’t re­view. The re­viewer does­n’t im­ple­ment.

✻ claude code · dy­namic work­flowad­ver­sar­ial re­view3 of the many bugs ad­ver­sar­ial re­view caught be­fore merge

bug 1 of 3 · the async close

✻claudeimplementer

its con­text: the .zig orig­i­nal, the port plan, its own rea­son­ing

Announcing TypeScript 7.0

devblogs.microsoft.com

Today we are proud to an­nounce the avail­abil­ity of TypeScript 7, a 10x faster na­tive port of TypeScript!

Since its early days, TypeScript has promised to de­liver on JavaScript that scales. By bring­ing strong type-check­ing and rich tool­ing to the world of JavaScript, TypeScript made it pos­si­ble to build non-triv­ial high-qual­ity apps across plat­forms.

Last year, our team un­veiled TypeScript’s next step in scal­ing: mak­ing every part of the toolset an or­der of mag­ni­tude faster. The mis­sion was a na­tive port of TypeScript built in Go that could make the most of mod­ern hard­ware. This port was done as faith­fully as pos­si­ble, writ­ing new code while main­tain­ing the struc­ture and logic of the orig­i­nal code­base to keep re­sults con­sis­tent and com­pat­i­ble be­tween the two com­pil­ers. The key dif­fer­ence is that with this new code­base, TypeScript 7 brings na­tive code speed, shared mem­ory mul­ti­thread­ing, and a num­ber of new op­ti­miza­tions that typ­i­cally yield speedups be­tween 8x and 12x on full builds.

Just as with any other re­lease, TypeScript 7 is avail­able via npm:

npm in­stall -D type­script

That will get you the new tsc ex­e­cutable in your work­space (which you can run via npx tsc). Of course, a big part of the TypeScript ex­pe­ri­ence is also about ed­i­tor sup­port. Your fa­vorite code ed­i­tor should eas­ily sup­port TypeScript 7 with its new sup­port for the lan­guage server pro­to­col (LSP), and its new speed and mul­ti­thread­ing im­prove­ments. Whether you’re us­ing some­thing like VS Code, Visual Studio, WebStorm, or any other mod­ern ed­i­tor, TypeScript 7 should work great. Just check your ed­i­tor’s doc­u­men­ta­tion — for ex­am­ple, VS Code has a ded­i­cated ex­ten­sion for TypeScript 7 that you can use to­day, and Visual Studio will au­to­mat­i­cally en­able TypeScript 7 based on your work­space.

What Does A Faster TypeScript Mean?

A faster TypeScript sounds great on pa­per, but what does it mean in prac­tice? Maybe it helps to think about where TypeScript comes up at every stage of de­vel­op­ment.

A typ­i­cal day of de­vel­op­ment might in­volve open­ing your ed­i­tor, open­ing a TypeScript file, and run­ning an op­er­a­tion like find-all-ref­er­ences across your pro­jects. Then as you’d start to make ed­its, maybe you’d ex­pect auto-com­ple­tions to pop up, and get red squig­gles on the fly as you’d make ed­its. When you (and more re­cently, per­haps an AI agent) were ready to build your pro­ject, you’d run tsc, check the out­put for er­rors, and then run your gen­er­ated code some­how.

A faster TypeScript means every part above is stream­lined. Waiting for your ed­i­tor to fully load your pro­ject will feel in­stan­ta­neous. Delays on find-all-ref­er­ences, auto-com­ple­tion, and di­ag­nos­tics should take a frac­tion of the time they used to. And when you run tsc, maybe in –watch mode, you’ll be able to tighten your feed­back loop and it­er­ate faster than ever be­fore.

You can see this on real-world pro­jects. In fact, you can try com­par­ing on a few open-source pro­jects your­self. Here are the build times of run­ning TypeScript 6 and 7 on some fairly large open source code­bases.

TypeScript 7 also typ­i­cally does bet­ter while ask­ing for less ag­gre­gate mem­ory over the span of a build.

Of course, there’s more to the ex­pe­ri­ence than the full build. On the same com­puter, open­ing a file with an er­ror in the VS Code code­base would pre­vi­ously take about 17.5 sec­onds from the time you opened the ed­i­tor to the time you saw the first er­ror. With TypeScript 7, it’s un­der 1.3 sec­onds — over 13x faster.

Battle-Tested and Ready for Production

The TypeScript pro­ject con­tains tens of thou­sands of tests built over more than a decade that run on every com­mit on our main branch. They’ve en­sured every one of our re­leases is sta­ble and re­li­able.

But TypeScript 7 is no or­di­nary re­lease. Beyond our test suite, we’ve lever­aged a num­ber of dif­fer­ent re­sources to make sure TypeScript 7 is solid for pro­duc­tion use.

Over the last year we’ve worked with many large teams in­ter­nally and ex­ter­nally to test TypeScript 7 on real-world code­bases. The re­sults have been over­whelm­ingly pos­i­tive, with en­tire com­pa­nies re­port­ing that TypeScript 7 has been sta­ble, fast, and easy to adopt. For ex­am­ple, the VS Code team re­cently high­lighted their ex­pe­ri­ence with TypeScript 7’s pre­view re­leases to move faster in their de­vel­op­ment cy­cle. We’ve also worked with Microsoft teams like Loop, Office, PowerBI, Teams, and Xbox to en­sure that TypeScript is ready for the largest of code­bases. Likewise, com­pa­nies like Bloomberg, Canva, Figma, Google, Lattice, Linear, Miro, Notion, Sentry, Slack, Vanta, Vercel, VoidZero, and more have worked with us to test TypeScript 7 on their code­bases and given us feed­back to make it bet­ter.

Additionally, we’ve re­built much of our broader test in­fra­struc­ture to run on TypeScript 7. TypeScript 6 and ear­lier had au­to­mated and on-de­mand test­ing for TypeScript and JavaScript pro­jects on GitHub to de­tect re­gres­sions in the com­piler and lan­guage ser­vice. The same test­ing is back, and run­ning against TypeScript 7, find­ing is­sues in real code­bases so we can find gaps in our core test suite and ship a bet­ter ex­pe­ri­ence.

The com­bi­na­tion of ex­plicit feed­back, au­to­mated crash re­ports, and ag­gres­sive test­ing has made a mea­sur­able dif­fer­ence in qual­ity. In fact, our data in­sights have shown us that TypeScript 7.0’s new lan­guage server has ac­tu­ally re­duced fail­ing lan­guage server com­mands by over 80%, and re­duced server crashes by over 60% com­pared to that of TypeScript 6.0.

We’ve also heard some in­cred­i­ble feed­back from teams at scale:

Slack en­gi­neers have told us that TypeScript 7 elim­i­nated 40% of their merge queue time and brought type-check­ing time in CI from about 7.5 min­utes to 1.25 min­utes. Local de­vel­op­ment in the ed­i­tor was pre­vi­ously al­most unusable” due to lan­guage server load times and en­gi­neers would typ­i­cally let CI do a full type-check. TypeScript 7 has been able to load the same code­base in a few sec­onds and made lo­cal type-check­ing fea­si­ble again.

Builds at Vanta have dra­mat­i­cally im­proved, show­ing a speedup of up to 9x faster on one of their biggest pro­jects.

Similarly, the News Services team at Microsoft told us that adopt­ing TypeScript 7 saved them 400 hours a month wait­ing for CI builds.

Last year, en­gi­neers work­ing on PowerBI de­scribed TypeScript 7 in the ed­i­tor as life-saving” for work­ing on their code­base. They adopted the ex­pe­ri­ence as a de­fault even be­fore TypeScript 7 sup­ported re­name func­tion­al­ity in VS Code.

Developers work­ing on Loop’s monorepo were also ec­sta­tic. The pre­vi­ous ed­i­tor ex­pe­ri­ence was de­scribed as un­us­able at their scale, whereas the TypeScript 7 ex­pe­ri­ence has been amazing” to use.

Canva de­vel­op­ers have told us that TypeScript 7’s lan­guage ser­vice shows dra­matic speedups, go­ing from about 58 sec­onds to see­ing the first er­ror in their ed­i­tors to about 4.8 sec­onds.

Running Side-by-Side with TypeScript 6.0

While TypeScript 7.0 is here, it does not ship with an API. We ex­pect TypeScript 7.1 to ship with a new (and dif­fer­ent) API, but un­til then we have made it a pri­or­ity to en­sure TypeScript can be run side-by-side with TypeScript 6.0 for util­i­ties that still need some pro­gram­matic ac­cess to the com­piler (such as type­script-es­lint).

As part of the 6.0/7.0 tran­si­tion process, we’ve pub­lished a new com­pat­i­bil­ity pack­age, @typescript/typescript6. This pack­age pro­vides an ex­e­cutable named tsc6, so that if needed, you can in­stall TypeScript 7.0 (which ships its own tsc bi­nary) side-by-side with­out nam­ing con­flicts. The new pack­age also re-ex­ports the TypeScript 6.0 API, so that you can use tsc for TypeScript 7, while other tool­ing can con­tinue to rely on 6.0.

Because some tools like type­script-es­lint ex­pect to im­port from type­script di­rectly via peer de­pen­den­cies, we rec­om­mend achiev­ing this via npm aliases. You should be able to run the fol­low­ing com­mand

npm in­stall -D type­script@npm:@type­script/​type­script6

or mod­ify your pack­age.json as fol­lows:

{ devDependencies”: { typescript”: npm:@typescript/typescript6@^6.0.2”, } }

Note that do­ing this will leave you only with a tsc6 ex­e­cutable. To get 7.0’s tsc, you can add an­other alias for TypeScript 7 and npx tsc will just work with 7.0:

{ devDependencies”: { @typescript/native”: npm:typescript@^7.0.2”, typescript”: npm:@typescript/typescript6@^6.0.2” } }

Nightly Builds and @typescript/native-preview

Until now, most de­vel­op­ers have in­stalled TypeScript 7 via the @typescript/native-preview pack­age. This pack­age shipped nightly builds of the new code­base, and has served the com­mu­nity well with over 8.5 mil­lion weekly down­loads!

However, go­ing for­ward, nightly builds will soon re­sume un­der the stan­dard type­script pack­age with the next tag. You can in­stall it with:

npm in­stall -D type­script@next

Custom Scaling: Parallelization and Controls

TypeScript 7.0 now per­forms many steps in par­al­lel, in­clud­ing pars­ing, type-check­ing, and emit­ting. Some of these steps, like pars­ing and emit­ting can mostly be done in­de­pen­dently across files. As such, par­al­leliza­tion au­to­mat­i­cally scales well with larger code­bases with rel­a­tively lit­tle over­head. But not every step in a TypeScript build is eas­ily par­al­leliz­able.

TypeScript 7 in­tro­duces the ex­per­i­men­tal –checkers and –builders flags to fine-tune the par­al­leliza­tion be­hav­ior for less-triv­ial steps like type-check­ing and pro­ject ref­er­ence build­ing. It also in­tro­duces a –singleThreaded flag to dis­able par­al­leliza­tion en­tirely, which can be use­ful for de­bug­ging or run­ning in en­vi­ron­ments with lim­ited re­sources.

Type-Checker Parallelization

Other steps, like type-check­ing, have more com­plex de­pen­den­cies across files. Most files end up re­ly­ing on the same type in­for­ma­tion from their de­pen­den­cies and the global scope, and so run­ning type-check­ers com­pletely in­de­pen­dently would be waste­ful — both in com­pu­ta­tion and mem­ory. On the other hand, type-check­ing oc­ca­sion­ally re­lies on the rel­a­tive or­der­ing of in­for­ma­tion in a pro­gram, and so type-check­ing from scratch must al­ways check the same files in an iden­ti­cal or­der to en­sure the same re­sults.

To en­able par­al­leliza­tion while avoid­ing these pit­falls, TypeScript 7.0 cre­ates a fixed num­ber of type-checker work­ers with their own view of the world. These type-check­ing work­ers may end up du­pli­cat­ing some com­mon work, but given the same in­put files, they will al­ways di­vide them iden­ti­cally and pro­duce the same re­sults.

The de­fault num­ber of type-check­ing work­ers is 4, but it can be con­fig­ured with the new –checkers flag. You may find that in­creas­ing this num­ber can fur­ther speed up builds on larger code­bases where typ­i­cal ma­chines have more CPU cores, but will typ­i­cally come at the cost of in­creased mem­ory us­age. For ex­am­ple, in the table above, we ran TypeScript 7 with its de­fault of –checkers 4. Here’s what the re­sults look like on the same ma­chine with –checkers 8.

As you can see, these code­bases get a bet­ter speedup from ded­i­cat­ing more cores, but re­sults will dif­fer across pro­jects and un­der­ly­ing ma­chines.

On the other hand, on ma­chines with fewer CPU cores and less mem­ory (e.g. CI run­ners) you may want to de­crease this num­ber to avoid un­nec­es­sary or in­ci­den­tal over­head. You can spec­ify a value as low as –checkers 1, ef­fec­tively mak­ing type-check­ing sin­gle-threaded and elim­i­nat­ing du­pli­cate work.

In rare cases, vary­ing the num­ber of –checkers may sur­face or­der-de­pen­dent re­sults. Specifying a fixed num­ber of check­ers across build en­vi­ron­ments can help en­sure every­one is get­ting the same re­sults, but is up to the dis­cre­tion of your team.

Project Reference Builder Parallelization

TypeScript 7.0 can par­al­lelize builds within a pro­ject, but it can now also build mul­ti­ple pro­jects at once as well. This be­hav­ior can be con­fig­ured with the new –builders flag, which con­trols the num­ber of par­al­lel pro­ject ref­er­ence builders that can run at once when run­ning un­der –build. This can be par­tic­u­larly help­ful for monore­pos with many pro­jects.

Like –checkers, in­creas­ing the num­ber of builders can speed up builds, but may come at the cost of in­creased mem­ory us­age. It also has a mul­ti­plica­tive ef­fect with –checkers, so it’s im­por­tant to find the right bal­ance for your ma­chine and code­base. For ex­am­ple, build­ing with –checkers 4 –builders 4 al­lows up to 16 type-check­ers to run at once, which may be ex­ces­sive.

Unlike –checkers, vary­ing the num­ber of builders should not pro­duce dif­fer­ent re­sults; how­ever, build­ing pro­ject ref­er­ences is fun­da­men­tally bot­tle­necked by the de­pen­dency graph of pro­jects (with the ex­cep­tion of type-check­ing on code­bases that lever­age –isolatedDeclarations and sep­a­rate syn­tac­tic de­c­la­ra­tion file emit).

Single-Threaded Mode

In some cases, it can be help­ful to en­force sin­gle-threaded op­er­a­tion through­out the com­piler. This may be use­ful for de­bug­ging, com­par­ing per­for­mance with TypeScript 6 and 7, when or­ches­trat­ing par­al­lel builds ex­ter­nally, or for run­ning in en­vi­ron­ments with very lim­ited re­sources. To en­able sin­gle-threaded mode, you can use the new –singleThreaded flag. This will not only cap the num­ber of type-check­ing work­ers to 1, but also en­sure pars­ing and emit­ting are done in a sin­gle thread.

Improved –watch Mode

TypeScript 7 ships with a com­pletely re­built –watch mode. –watch is now pow­ered by a new foun­da­tion based on the Parcel bundler’s file-watcher that pro­vides ef­fi­cient and sta­ble cross-plat­form file watch­ing ca­pa­bil­i­ties.

When our team set out to port our file watch­ing logic, we en­coun­tered a few chal­lenges with cross-plat­form file watch­ing in Go. The stan­dard li­brary does­n’t pro­vide a built-in file watch­ing API, and ex­ist­ing third-party li­braries we ex­plored had var­i­ous is­sues with sta­bil­ity, per­for­mance, cross-plat­form sup­port, or is­sues with build tool­ing in­te­gra­tion. We were able to build so­lu­tions around polling pe­ri­od­i­cally to check for file changes, and this worked broadly across op­er­at­ing sys­tems; how­ever it was com­pu­ta­tion­ally ex­pen­sive, es­pe­cially at larger-scale pro­jects with many de­pen­den­cies in node_­mod­ules. Even with dy­namic sched­ul­ing strate­gies, we found that pure-polling so­lu­tions were too tax­ing for gen­eral use.

For many years, Visual Studio Code has re­lied on @parcel/watcher, and in re­cent years TypeScript in VS Code has re­lied on its file watch­ing ca­pa­bil­i­ties in­di­rectly. While it seemed promis­ing, one of the prob­lems for us with Parcel’s watcher is that it’s writ­ten in C++, and in turn re­quires a full C++ tool­chain to build. Given our pos­i­tive ex­pe­ri­ence with Parcel’s watcher in VS Code, we ex­plored port­ing it to Go with a few min­i­mal as­sem­bly shims to avoid in­tro­duc­ing a new tool­chain de­pen­dency.

The ex­plo­ration has been a suc­cess — what started as a very di­rect trans­la­tion from C++ to Go was fur­ther re­fined into id­iomatic Go that still passes the ported test suite. The watcher is a self-con­tained pack­age that has al­lowed us to keep a clean sep­a­ra­tion of con­cerns be­tween what we care to watch and why. We are now see­ing sig­nif­i­cant re­source im­prove­ments in –watch mode across plat­forms, and have been hear­ing pos­i­tive feed­back from ear­lier users of TypeScript 7.

We’d like to ex­tend our thanks to Devon Govett whose work on Parcel has pro­vided im­mense ben­e­fits to both the Visual Studio Code and TypeScript pro­jects. We hope this port will pro­vide op­por­tu­ni­ties and in­sights for the orig­i­nal Parcel watcher code­base over time.

Updates Since 5.x, and New Behaviors from 6.0

TypeScript 7.0 is made to be com­pat­i­ble with TypeScript 6.0’s type-check­ing and com­mand-line be­hav­ior. Practically any TypeScript code that com­piles cleanly with TypeScript 6.0 (with the sta­ble­Type­Order­ing flag on, and with­out any ig­nore­Dep­re­ca­tions flag set) should com­pile iden­ti­cally in TypeScript 7.0.

With that said, TypeScript 7.0 adopts 6.0’s new de­faults, and pro­vides hard er­rors in the face of any flags and con­structs dep­re­cated in TypeScript 6.0. This is no­table as 6.0 is still rel­a­tively new, and many pro­jects will need to adapt to its new be­hav­iors. We en­cour­age de­vel­op­ers to adopt TypeScript 6.0 to make the tran­si­tion to TypeScript 7.0 eas­ier, and you can also read the TypeScript 6.0 re­lease blog post for more de­tails on these dep­re­ca­tions.

At a glance, the no­table de­fault changes to con­fig­u­ra­tion are:

strict is true by de­fault.

mod­ule de­faults to es­next.

tar­get de­faults to the cur­rent sta­ble ECMAScript ver­sion im­me­di­ately pre­ced­ing es­next.

noUnchecked­Side­Ef­fectIm­ports is true by de­fault.

li­bRe­place­ment is false by de­fault.

sta­ble­Type­Order­ing is true by de­fault, and can­not be turned off.

root­Dir now de­faults to ./, and in­ner source di­rec­to­ries must be ex­plic­itly set.

types now de­faults to [], and the old be­hav­ior can be re­stored by set­ting it to [“*”].

We be­lieve the root­Dir and types changes may be the most surprising” changes, but they can be mit­i­gated eas­ily. Projects where the tscon­fig.json sits out­side of a di­rec­tory like src will sim­ply need to in­clude root­Dir to pre­serve the same di­rec­tory struc­ture.

{ compilerOptions”: { // … + rootDir”: ./src” }, include”: [”./src”] }

For the types change, pro­jects that de­pend on spe­cific global de­c­la­ra­tions will need to list them ex­plic­itly. For ex­am­ple,

{ compilerOptions”: { // Explicitly list the @types pack­ages you need (e.g. bun, mocha, jas­mine, etc.) + types”: [“node”, jest”] } }

The dep­re­ca­tions that have turned into hard er­rors with no-op be­hav­ior are:

tar­get: es5 is no longer sup­ported.

down­levelIt­er­a­tion is no longer sup­ported.

mod­uleRes­o­lu­tion: node/​node10 are no longer sup­ported, with nodenext and bundler be­ing rec­om­mended in­stead.

mod­ule: amd, umd, sys­temjs, none are no longer sup­ported, with es­next or pre­serve be­ing rec­om­mended in con­junc­tion with bundlers or browser-based mod­ule res­o­lu­tion.

baseUrl is no longer sup­ported, and paths can be up­dated to be rel­a­tive to the pro­ject root in­stead of baseUrl.

mod­uleRes­o­lu­tion: clas­sic is no longer sup­ported, and bundler or nodenext are the rec­om­mended re­place­ments.

es­Mod­uleIn­terop and al­lowSyn­thet­icDe­fault­Im­ports can­not be set to false.

al­waysStrict is as­sumed to be true and can no longer be set to false.

The mod­ule key­word can­not be used in name­space de­c­la­ra­tions.

The as­serts key­word can­not be used on im­ports, and must use the with key­word in­stead (to align with de­vel­op­ments on ECMAScript’s im­port at­tribute syn­tax).

/// <reference no-de­fault-lib /> di­rec­tives are no longer re­spected un­der skipDe­fault­LibCheck.

Command line builds can­not take file paths when the cur­rent di­rec­tory con­tains a tscon­fig.json file un­less passed an ex­plicit –ignoreConfig flag.

Template Literal Types Now Preserve Unicode Code Points

TypeScript 7.0 now treats Unicode code points more nat­u­rally when in­fer­ring from tem­plate lit­eral types. For ex­am­ple:

type HeadTail<S> = S ex­tends `${infer Head}${infer Tail}` ? [Head, Tail] : never;

type Result = HeadTail<“😀abc”>; // ^ // In 7.0: [“😀, abc”] // Previously: [“\ud83d”, \ude00abc”]

Previously, TypeScript fol­lowed JavaScript’s UTF-16 in­dex­ing be­hav­ior here and split 😀 into two halves of a sur­ro­gate pair (\ud83d and \ude00). That was tech­ni­cally con­sis­tent with in­dex­ing in JavaScript (e.g. the in­ferred Head type was equal to 😀abc”[0]), but it usu­ally was­n’t what peo­ple in­tended, and could pro­duce string lit­eral types con­tain­ing un­paired sur­ro­gates that aren’t se­man­ti­cally mean­ing­ful.

This is a break­ing change for type-level string ma­nip­u­la­tion that in­ten­tion­ally mod­eled UTF-16 code units, such as some string Length util­i­ties. In prac­tice, we ex­pect the new be­hav­ior to be more use­ful and less sur­pris­ing: tem­plate lit­eral in­fer­ence now fol­lows the same in­tu­ition as it­er­at­ing a string with for…of or spread­ing it with […str], where 😀 is treated as one unit.

JavaScript Differences

As we ported the ex­ist­ing code­base, we also took the op­por­tu­nity to re­visit how our JavaScript sup­port works.

TypeScript orig­i­nally sup­ported JavaScript files by us­ing JSDoc com­ments and rec­og­niz­ing cer­tain code pat­terns for analy­sis and type in­fer­ence. Lots of the time, this was based on pop­u­lar cod­ing pat­terns, but oc­ca­sion­ally it was based on what­ever peo­ple might be writ­ing that Closure and the JSDoc doc gen­er­at­ing tool might un­der­stand. While this ap­proach was help­ful for de­vel­op­ers with loosely-writ­ten JSDoc code­bases, it re­quired a num­ber of com­pro­mises and spe­cial cases to work well, and di­verged in a num­ber of ways from TypeScript’s analy­sis in .ts files.

In TypeScript 7.0, we have re­worked our JavaScript sup­port to be more con­sis­tent with how we an­a­lyze TypeScript files. Some of the dif­fer­ences in­clude:

18 Words - Daily Word Challenge

18words.com

Thanks for play­ing! You can send me feed­back or try my other game Zanagrams.

My Thoughts on the Bun Rust Rewrite

andrewkelley.me

Context: Rewriting Bun in Rust

History

When Jarred joined the Zig com­mu­nity about 5 years ago, I de­scribed him as some­one who had strong beginner en­ergy”. That is, he moved fast and tried a lot of dif­fer­ent stuff, jump­ing head first into prob­lems that he was not yet equipped to solve, lead­ing to mediocre out­comes in terms of en­gi­neer­ing, but learn­ing a whole heck of a lot in the process. I see it as quite a healthy at­ti­tude, par­tic­u­larly for young peo­ple and stu­dents. This is the best way to level up and learn new things.

As he fo­cused his ef­forts on Bun he be­gan to at­tract at­ten­tion. JavaScript be­ing the most pop­u­lar pro­gram­ming lan­guage in the world, there are a lot of po­ten­tial eye­balls on a promis­ing new tool­chain.

This at­ten­tion could have been har­nessed in a few dif­fer­ent ways. For ex­am­ple, he could have eas­ily achieved a solid liv­ing via crowd­fund­ing, even for San Francisco stan­dards. But hav­ing grad­u­ated from the Thiel Fellowship school of thought rather than uni­ver­sity, he was es­sen­tially groomed from a young age into un­crit­i­cally em­brac­ing the Silicon Valley mind­set, and he took ven­ture cap­i­tal.

From the be­gin­ning, Jarred was ap­pre­cia­tive to­wards the Zig pro­ject. He cred­ited Zig on the Bun web­site for the pro­jec­t’s per­for­mance achieve­ments. He set up a monthly do­na­tion to Zig Software Foundation that amounted to $60,000 per year. He did­n’t have to do ei­ther of those things, but he did, and it was pretty cool of him. Even in his blog post that I’m ref­er­enc­ing, he ex­presses what I per­ceive as sin­cere grat­ti­tude to­wards the Zig pro­ject.

However, once Bun be­came a VC-backed startup, he started rac­ing to­wards the fin­ish line. Now, in­stead of work­ing on a free and open source pro­ject, learn­ing and grow­ing with the com­mu­nity, Jarred was run­ning a busi­ness. It was at this point - when he sud­denly be­came a man­ager - that this beginner en­ergy” started to hit dif­fer­ently for me. It’s one thing to choose a poor work-life bal­ance for one­self; a dif­fer­ent thing en­tirely to de­mand it of oth­ers:

Oven is go­ing to be a grind, es­pe­cially the first nine months or so. If work-life bal­ance means a lot of time spent not work­ing, it’s prob­a­bly not a good fit.”

Fun fact: peo­ple talk to each other.

I talked to those who in­ter­viewed for a job at Oven. I talked to peo­ple who worked there. Those peo­ple talked to each other. Everybody talked to every­body. The grapevine was large and healthy and full of juicy grapes, and all those grapes con­tained the juice of the same mes­sage: Jarred was a stinky man­ager. Poor com­mu­ni­ca­tion, un­re­al­is­tic ex­pec­ta­tions, low em­pa­thy, no ex­pe­ri­ence. Just a to­tal shit show, from an em­ploy­ment per­spec­tive.

Consequently, al­though Zig com­mu­nity mem­bers were ea­ger to find work cod­ing in Zig on the clock, most of the tal­ent pool steered clear of Oven and Bun.

At the same time, a rift be­tween Zig and Jarred started widen­ing. His sin­gu­lar fo­cus on pro­duc­tiv­ity and his star­tup’s exit strat­egy was in­creas­ingly at odds with my longer term vi­sion for the Zig pro­ject. I re­mem­ber he kept nag­ging me to drop all my other pri­or­i­ties and work on a Language Server Protocol im­ple­men­ta­tion and VSCode in­te­gra­tion, while I had big­ger plans.

The main prob­lem, how­ever, was code qual­ity.

The Zig team reg­u­larly checks in on our users’ pro­jects. We read source code to find out how the lan­guage is af­fect­ing users, we test changes to see how prob­lem­atic break­age might be, and we check for per­for­mance re­gres­sions.

We be­came in­creas­ingly hor­ri­fied at the pro­gram­ming prac­tices we saw in Bun’s code­base. Hacks on top of hacks. Abuse of as­ser­tions. Most of all, reck­lessly speed­ing past fea­ture af­ter fea­ture with very lit­tle time taken for re­flec­tion and elim­i­na­tion of bugs and tech­ni­cal debt. Jarred was al­ready writ­ing slop well be­fore he had ac­cess to LLMs. Now, it’s not our busi­ness to po­lice what our users do, but you may have no­ticed peo­ple scream­ing in our faces about mem­ory safety con­stantly. You can imag­ine how we might want to put some so­cial dis­tance be­tween our­selves and a pro­ject whose ir­re­spon­si­ble soft­ware en­gi­neer­ing prac­tices in­vite the ex­act kind of crit­i­cism that peo­ple are ea­ger to level.

We made fu­tile at­tempts to guide them to­wards bet­ter pro­gram­ming prac­tices. There were a few ex­cep­tional he­roes who did their very best in a dys­func­tional com­pany. You know who you are. But you can’t stop a ris­ing tide.

By this time, we all felt at ZSF that Bun was a net li­a­bil­ity, and this was be­fore RoboBun be­came the #1 con­trib­u­tor. Along with the dis­com­fort of the pub­licly pre­sumed poster child for Zig pro­gram­ming lan­guage ac­tu­ally be­ing the prime ex­am­ple of How Not To Write Zig Code, at some point they would sell out (let’s be hon­est, their vague sell some cloud some­thing” busi­ness plan was a farce from the get-go), we would re­ceive some neg­a­tive pub­lic­ity by proxy, and we’d stop get­ting that reg­u­lar do­na­tion.

So, when the Anthropic aqui­si­tion fi­nally hap­pened, we at ZSF breathed a sigh of re­lief. When the do­na­tion silently stopped, our bank ac­count was ready for it. When they nei­ther can­celed their monthly meet­ing with us, nor showed up, we were not sur­prised. The re­la­tion­ship was over.

The (re)writing was on the wall. Even within a cou­ple days, we al­ready sus­pected a Rust rewrite was com­ing. And we were root­ing for it! The ac­qui­si­tion by a large AI com­pany was a bur­den, be­cause even the in­di­rect con­nec­tion of Claude be­ing writ­ten in Bun be­ing writ­ten in Zig caused not only a surge of drive by slop con­tri­bu­tions, but also an in­flux of taste­less AI en­thu­sists into Zig com­mu­ni­ties who had to be in­formed that it’s an­ti­so­cial to paste LLM out­put into fo­rum posts. For a mo­ment, I feared Zig’s iden­tity would be­come known col­lo­qui­ally as a pro­gram­ming lan­guage as­so­ci­ated with AI.

When Jarred an­nounced the Rust rewrite, we were ec­sta­tic. It seemed too good to be true. I have to ad­mit, I did­n’t think the tech­nol­ogy was there, to pull off this stunt. But he did it, and now I’m metaphor­i­cally sip­ping de­li­cious tea from a mug that says It Tastes Like It’s Not My Problem Anymore”.

Addressing the Blog Post

The blog post is ex­pertly writ­ten. It’s al­most like the mar­ket­ing de­part­ment of a tril­lion dol­lar com­pany has a lot of money rid­ing on this ar­ti­cle.

I do have some bones to pick how­ever.

There’s a di­chotomy be­ing pre­sented here where you have to ei­ther choose a style guide” or a pro­gram­ming lan­guage fea­ture in or­der to avoid bugs. The sleight of hand mis­di­rects the reader away from the main way bugs are elim­i­nated: by ded­i­cat­ing en­gi­neer­ing re­sources to it. You’re not giv­ing TigerBeetle nearly enough credit. Quite sim­ply they put in the time to find and elim­i­nate the bugs, they make an ef­fort to main­tain a healthy re­la­tion­ship with ZSF, and Bun did not do that.

The ar­gu­ment for ship­ping all the mil­lion lines of un­re­viewed code is that the test suite is good enough to catch every­thing. Then why are you say­ing you have so many an­noy­ing bugs in the Zig code? What hap­pened to the test suite be­ing suf­fi­cient to catch every­thing? It’s not suf­fi­cient to catch bugs in Zig code but it is suf­fi­cient to catch bugs in 1 mil­lion lines of un­re­viewed slop?

Performance in­crease is at­trib­uted to LTO, which Zig has sup­ported for all of Bun’s ex­is­tence. It used to be en­abled by de­fault un­til we ran into too many LLVM bugs, all of which also af­fect Rust. We prob­a­bly tried to tell you to try en­abling it and you did­n’t lis­ten. We have good ad­vice, damn it!

The post claims they were fuzzing their Zig code, while dur­ing our calls the whole Bun team told us that they were not fuzzing any­thing. This ap­pears to be an out­right fab­ri­ca­tion.

The blog post out­lines a bunch of en­gi­neer­ing work done to re­duce bi­nary size, to bet­ter make the case that Bun is bet­ter in Rust”. But all that en­gi­neer­ing work had noth­ing to do with the rewrite. I think this is pre­cisely why it took so long for the blog post to come out, you were do­ing the en­gi­neer­ing work that you should have done in the Zig code­base since the be­gin­ning. We’ve been try­ing to warn you about your comp­time abuse for years. We even made this time re­port thing specif­i­cally for pro­jects that need to au­dit their use of comp­time/​in­line us­age and com­pile times.

I no­ticed that you ne­glected to men­tion com­pi­la­tion speed. Zig com­piler pro­ject is about 600,000 lines of code - roughly the same size as Bun be­fore the rewrite, and I’m clock­ing 16s to build from scratch with a clean cache, fol­lowed by 90ms for each sub­se­quent edit with in­cre­men­tal com­pi­la­tion en­abled. What are the cor­re­spond­ing mea­sure­ments of Bun post-rewrite?

What Did We Learn Here Today?

Zooming out a bit, I want to make a few things clear.

One, I’m gen­uinely grate­ful for the do­na­tions ZSF re­ceived from Bun. We spent that money pay­ing con­trib­u­tors to work on Zig.

Two, I ac­tu­ally don’t have any per­sonal crit­i­cisms of Jarred. He has dif­fer­ent taste than me, he wants dif­fer­ent things out of life than me. But I think he’s ac­tu­ally happy and suc­cess­ful ex­actly where he is. He fig­ured out how to ac­com­plish all the stuff in life that he wants. He gets to live out his pro­duc­tiv­ity fan­tasy fever dream, he’s prob­a­bly al­ready su­per wealthy. He has mi­nor tech celebrity sta­tus.

Honestly, I think he did well for him­self, and I don’t wish him any ill will.

That said I’m happy that our busi­ness in­ter­ests are no longer in­ter­twined! As soon as the Internet stops ar­gu­ing in pub­lic about whether the rewrite was good or bad for Bun based on the lan­guage choice, I be­lieve that con­cludes our in­ter­ac­tions.

¯\_(ツ)_/¯

Thanks for read­ing my blog post.

Cloudflare Drop

www.cloudflare.com

EU Parliament greenlights Chat Control 1.0 – Breyer: "Our children lose out"

www.patrick-breyer.de

Today, the European Parliament al­lowed the sus­pi­cion­less mass scan­ning of pri­vate com­mu­ni­ca­tions (“Chat Control 1.0”) to pass, a mea­sure it had re­jected twice in March. Although a ma­jor­ity of vot­ing Members of the European Parliament (MEPs) ac­tu­ally op­posed the reg­u­la­tion (314 against, 276 in fa­vor, 17 ab­sten­tions), the mo­tion to re­ject it failed to se­cure the re­quired ab­solute ma­jor­ity of 361 votes. As a re­sult, mass scan­ning is now per­mit­ted again un­til 2028.

A sym­bolic ex­emp­tion was adopted for en­crypted com­mu­ni­ca­tions—though in prac­tice, ser­vice providers do not scan these any­way. Furthermore, while a ma­jor­ity of vot­ing MEPs wanted to re­strict the scan­ning of pri­vate com­mu­ni­ca­tions strictly to sus­pects iden­ti­fied by the ju­di­ciary (322 to 255 votes), this amend­ment like­wise fell short of the re­quired ab­solute ma­jor­ity.

Dr. Patrick Breyer, civil rights ac­tivist and for­mer Member of the European Parliament (MEP), warns of the con­se­quences:

The fact that Chat Control is mov­ing for­ward against the will of the ma­jor­ity of vot­ing MEPs is a farce and dam­ages democ­racy. Our chil­dren are the real losers in this un­de­mo­c­ra­tic process. The pas­sage of a gen­uine, per­ma­nent child pro­tec­tion reg­u­la­tion is now in se­ri­ous jeop­ardy. The Council will never agree to a des­per­ately needed par­a­digm shift as long as they can sim­ply stick to the old ap­proach of sus­pi­cion­less scan­ning at the whim of the tech in­dus­try.”

Despite the leg­isla­tive de­feat, Breyer re­mains de­fi­ant re­gard­ing the up­com­ing ne­go­ti­a­tions:

Today’s vote on the in­terim reg­u­la­tion was a set­back, but the po­lit­i­cal bat­tle over the per­ma­nent Chat Control 2.0’ is just get­ting started. The re­sis­tance we saw in Parliament to­day was so strong that find­ing a ma­jor­ity for per­ma­nent, sus­pi­cion­less mass scan­ning in fu­ture ne­go­ti­a­tions is a com­plete pipe dream.”

Breyer fun­da­men­tally re­jects the mass sur­veil­lance ap­proach:

Trying to pro­tect chil­dren with sus­pi­cion­less mass sur­veil­lance is like fran­ti­cally mop­ping the floor while the faucet is still run­ning. Blanket chat con­trol is just as un­ac­cept­able as in­dis­crim­i­nately open­ing every­one’s phys­i­cal mail. For five years, this failed sys­tem has served as a smoke­screen to de­lay real ac­tion, all while over­whelm­ing the po­lice with false alarms. We need more child pro­tec­tion, not less—but we need ef­fec­tive pro­tec­tion, not the il­lu­sion of se­cu­rity.”

What hap­pens next?The in­terim reg­u­la­tion passed to­day will re­main in ef­fect un­til 2028, or un­til an agree­ment on a per­ma­nent reg­u­la­tion is reached. Negotiations for the per­ma­nent law will re­sume in September. The core dis­pute be­tween the EU Parliament, mem­ber state gov­ern­ments, and the EU Commission re­mains the scan­ning of pri­vate chats: should it be in­dis­crim­i­nate, or tar­geted at crim­i­nal sus­pects?

What changes with the re­turn of Chat Control 1.0—and what stays the same:

What is com­ing back: US tech com­pa­nies are once again al­lowed to scan pri­vate mes­sages with­out a war­rant or prior sus­pi­cion. This af­fects di­rect mes­sages on plat­forms like Instagram, Discord, Snapchat, Skype, and Xbox, as well as emails via Google’s Gmail and Apple’s iCloud.

What re­mains un­changed: Public so­cial me­dia posts and files hosted in cloud stor­age could al­ready be scanned with­out this law. Furthermore, pri­vate mes­sages can al­ways be re­ported by users, or mon­i­tored by au­thor­i­ties us­ing tar­geted, court-or­dered wire­tap­ping.

What is still NOT be­ing scanned: End-to-end en­crypted chats, such as those on WhatsApp, have al­ways been ex­empt from these scans. Additionally, European providers of mes­sag­ing and email ser­vices have never im­ple­mented chat con­trol mea­sures.

Why Chat Control is the wrong ap­proach:

Since 2022, the vol­ume of sus­pected abuse re­ports from the US has al­ready dropped by 50 per­cent due to the grow­ing use of mes­sage en­cryp­tion.

According to EU Commission fig­ures, mass scan­ning of pri­vate chats ac­counted for only 36 per­cent of all abuse re­ports in 2024 (the ma­jor­ity came from pub­lic posts and cloud stor­age).

The German Federal Criminal Police Office (BKA) re­ports that 48 per­cent of all in­com­ing alerts are not crim­i­nally rel­e­vant in the first place.

Crime sta­tis­tics re­veal that 40 per­cent of the re­sult­ing in­ves­ti­ga­tions ac­tu­ally tar­get mi­nors them­selves.

Under the chat con­trol sys­tem, an es­ti­mated 99 per­cent of re­ports gen­er­ated by Meta con­sist of pre­vi­ously known ma­te­r­ial, which gen­er­ally does lit­tle to stop on­go­ing, ac­tive abuse.

The EU Commission ad­mits there is no ev­i­dence that sus­pi­cion­less scan­ning of pri­vate com­mu­ni­ca­tions has led to an in­crease in crim­i­nal con­vic­tions or in res­cued chil­dren.

Talk of avert­ing a protection gap” is there­fore highly mis­lead­ing. The most ef­fec­tive law en­force­ment tools—court-or­dered wire­taps, user re­ports, and the scan­ning of pub­lic plat­forms and cloud stor­age—were never at risk and re­main fully in­tact. The only prac­tice that was tem­porar­ily banned since April was the in­dis­crim­i­nate, war­rant­less search­ing of pri­vate, un­en­crypted mes­sages of in­no­cent peo­ple on a hand­ful of US plat­forms.

Background: The dead­lock over a per­ma­nent so­lu­tionIn par­al­lel, ne­go­ti­a­tions are on­go­ing for a per­ma­nent reg­u­la­tion to pro­tect chil­dren from sex­u­al­ized on­line vi­o­lence (the CSAM Regulation” or Chat Control 2.0”). In these talks, the EU Parliament is push­ing for a par­a­digm shift in how we ap­proach on­line child safety, de­mand­ing:

Mandatory, tar­geted de­tec­tion or­ders against ac­tual crim­i­nal sus­pects, rather than blan­ket mass scan­ning left to the tech in­dus­try’s dis­cre­tion.

An EU Child Protection Centre tasked with the sys­tem­atic re­moval of known abuse ma­te­r­ial from the pub­lic in­ter­net.

Strict se­cu­rity stan­dards for mes­sag­ing apps (“Security by Design”) to pre­vent cy­ber groom­ing.

This per­ma­nent leg­is­la­tion has stalled be­cause EU mem­ber states in­sist on main­tain­ing the out­dated ap­proach of vol­un­tary, sus­pi­cion­less scan­ning of pri­vate com­mu­ni­ca­tions. Critics warn that re­peat­edly ex­tend­ing the in­terim rules re­moves the po­lit­i­cal pres­sure needed to reach a vi­able, per­ma­nent agree­ment. Ultimately, cling­ing to the sta­tus quo threat­ens to de­rail real progress on child pro­tec­tion.

Patrick Breyer sums up the prob­lem:“As long as EU gov­ern­ments can use pro­ce­dural loop­holes to con­tin­u­ally ex­tend their com­fort­able sta­tus quo of vol­un­tary, in­dis­crim­i­nate mass scan­ning, they have zero in­cen­tive to en­gage with the Parliament’s tar­geted, legally sound, and far more ef­fec­tive child pro­tec­tion strat­egy.

The Voices of Survivors: We need pri­vacy to bring abusers to jus­tice”

Survivors of sex­ual vi­o­lence ex­plic­itly em­pha­size that un­tar­geted Chat Control did not help vic­tims:

Alexander Hanff, sur­vivor of child sex­ual abuse and pri­vacy ad­vo­cate, clar­i­fies:“As a sur­vivor I re­lied on con­fi­den­tial com­mu­ni­ca­tions to tell my story and find jus­tice for 28 school­boys—my­self in­cluded—re­sult­ing in the con­vic­tion of mul­ti­ple of­fend­ers. We sur­vivors need pri­vacy, be­cause with­out it we lose our voice. Chat Control was not cre­ated to pro­tect chil­dren. It was about Big Tech com­pa­nies like Meta or Google want­ing ac­cess to our data for prof­i­teer­ing, and states at­tempt­ing to ex­pand mass sur­veil­lance. The EU Commission has wasted five years and mil­lions of eu­ros on al­go­rithms that can­not pro­tect chil­dren and were never meant to. This money should have been di­verted to real polic­ing, causal re­search, and sup­port for sur­vivors, mil­lions of whom have never re­ceived any sup­port at all.”

Marcel Schneider* (name changed), a sur­vivor who has been su­ing Meta in court over its vol­un­tary Chat Control, adds:“Any­one mourn­ing the end of Chat Control has not un­der­stood what ac­tu­ally helps sur­vivors of sex­ual vi­o­lence. Mass sur­veil­lance by cor­po­ra­tions like Meta does not pre­vent abuse. Genuine pro­tec­tion means: delet­ing ma­te­r­ial at the source, proac­tive po­lice work on the Darknet, and apps that are safe by de­sign for chil­dren from the very start.”

Dorothée Hahne, found­ing mem­ber and vice-chair of the sur­vivors’ ini­tia­tive MOGiS e.V. (A Voice for Survivors), em­pha­sizes the dan­ger mass sur­veil­lance poses to vic­tims them­selves:“As sur­vivors, we see our safe spaces’, our pro­tected ar­eas and com­mu­ni­ca­tion chan­nels, en­dan­gered or de­stroyed by this. For sur­vivors, this need is ex­is­ten­tial.“

🐀 ESCAPE THE RAT RACE

www.abeyk.com

A FAANG™ Life Simulator

one tap = one quar­ter of your life · feat. MILTON

daily run = every­one gets the same cursed time­line to­day. com­pare es­capes.

not af­fil­i­ated with any tril­lion-dol­lar com­pany, al­legedly.

mil­ton ↑

AGE 22Y1 · Q1

🏝️ FREEDOM (quit-forever fund)0%

📈 per­for­mance50

🚀 trac­tion0

🔥 burnout10

NET WORTH$0k

COMP / YR$190k

we are so back

GO HOME

Form HR-1099-RAT · Exit Interview · —

one square = one year of your life:

Post on 𝕏

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.