
software engineer @vercel β’ I write about performance, compilers, puzzles, &more on my website β’ @recursecenter alum
- Tweets883
- Following928
- Followers1.4K
- Likes3.7K

ALT Screenshot of blog post: Below are the first four merges from the real goldshire trace. GPT-2 chooses the lowest-ranked valid adjacent pair (not the leftmost pair or the longest token). So r+e, which has rank 4, wins before the leading-space pair, even though it is near the end of the match. The important bit to take away is that each merge can expose another candidate. ...

ALT screenshot of terminal: β ./run.sh GPT-2 XL CODING AGENT Model: openai-community/gpt2-xl Device: mps Seed: 419 INITIAL PROMPT β GPT-2 ββββββββββββββββββββββββ You are a programmer controlling a tiny project. Choose one action at a time. Use exact filenames from Computer. After a FAIL names a file, READ that file next. Actions: ACTION: LIST ACTION: READ filename.py ACTION: WRITE filename.py CONTENT: complete file contents END ACTION: RUN COMMAND: python check.py ACTION: FINISH Example: Task: Fix the project so all checks pass. Programmer: ACTION: LIST Computer: check.py message.py names.py

ALT Screenshot of blog post: So the goal is to be able to write TypeScript code like this: async function readFile(path: string) { await sema.acquire(); defer sema.release(); // New! // ... use resource } The TypeScript Compiler The TypeScript compiler (tsc) is mostly a static analysis engine. Its complexity lies in type-checking a fundamentally dynamic language, and supporting extremely incremental compilation to meet latency expectations in an IDE. Lucky for us, we don't need to worry too much about types or other analysis in order to add our defer statement. tsc already has the machinery for "recognize syntax X, replace it with equivalent syntax Y."

ALT Screenshot of post: A Tiny Compiler for Data-Parallel Kernels Jun 2026 A lot of fast code starts as a boring loop. Modern hardware can perform the same operation on multiple values at once (e.g. SIMD and SIMT), and sometimes we write code directly for those execution models but other times, a compiler starts with regular-looking code and rewrites it so multiple loop iterations can run together. I built a tiny compiler (~180LOC of Python) to understand what that transformation looks like. My compiler lowers kernels (rewrites them into a simpler, more explicit form where data parallelism is visible). The input is a small hand-written AST, and the output is a lowered IR that I print as Python-like code. Rather than going all the way from source code to instructions, think of this compiler as an intermediate step in a larger compiler. Let's take a look at an example. Scaling audio is easy to parallelize, but it is still common to write non-explicitly parallel code like this:

ALT Screenshot of: Building a Runtime with QuickJS Mar 2026 A JavaScript engine (e.g. V8, JavaScriptCore) executes JavaScript code. It doesn't know about things like files, HTTP requests, or timers. On the other hand, a JavaScript runtime (e.g. Node.js, Bun) is a more complete environment where JavaScript runs. It contains a JavaScript engine, extra APIs, an event loop and task queues, and platform-specific features. That's what I'm hacking on today: a tiny runtime with console.log, process.uptime(), setTimeout and clearTimeout, fs.readFileSync and fs.readFile, as well as an event loop and worker pool for file I/O. Built on top of QuickJS. Here's an example program it can run: const startedAt = process.uptime();
![Screenshot of:
To recap a bit, I'll step through what happens when ls $HOME | grep foo is entered.
It's tokenized into ["ls", "$HOME", "|", "grep", "foo"].
Then expanded into ["ls", "/Users/andrew", "|", "grep", "foo"].
The flat token list is separated into structured pipeline commands:
["ls", "/Users/andrew"]
["grep", "foo"]
The shell creates a pipe to connect the output of ls to the input of grep.
The child commands start via fork, and execvp swaps them into the target programs.
By the wonderful design of Unix: if grep reads faster than ls writes, it blocks waiting for more data; if ls writes faster than grep reads, the pipe buffer fills and ls temporarily blocks. This synchronization happens automatically through the pipe, while the shell simply waits for both child processes to finish.
The output of grep isn't connected to a pipe as it's the last command, and any results are displayed to the user.](https://pbs.twimg.com/media/HDm8upfacAAg6Ak.jpg?name=small&format=webp)
ALT Screenshot of: To recap a bit, I'll step through what happens when ls $HOME | grep foo is entered. It's tokenized into ["ls", "$HOME", "|", "grep", "foo"]. Then expanded into ["ls", "/Users/andrew", "|", "grep", "foo"]. The flat token list is separated into structured pipeline commands: ["ls", "/Users/andrew"] ["grep", "foo"] The shell creates a pipe to connect the output of ls to the input of grep. The child commands start via fork, and execvp swaps them into the target programs. By the wonderful design of Unix: if grep reads faster than ls writes, it blocks waiting for more data; if ls writes faster than grep reads, the pipe buffer fills and ls temporarily blocks. This synchronization happens automatically through the pipe, while the shell simply waits for both child processes to finish. The output of grep isn't connected to a pipe as it's the last command, and any results are displayed to the user.

ALT A Fair, Cancelable Semaphore in Go Dec 2025 They say that you don't fully understand something unless you can build it from scratch. To wit, my challenge to the more technical readers of this blog is: can you build a semaphore from scratch in your favorite programming language? Bonus points for also handling context cancellation. I attempted this in Go and it was about 5x harder than I thought it would be. Largely due to concurrency/locking bugs β I assume you'll have an easier time in, say, JavaScript.



