Latest Posts (20 found)
Unsung Today

“Rather than fighting my tendency to type these two characters in my omnibar, I neutralized it.”

An interesting short story from Ernie Smith at Tedium, who ventured out to do the opposite of what we usually cover on this blog – disrespect his motor memory : Recently, I made a realization: I have been unwillingly addicted to Facebook for a long time, and it’s not even because I like Facebook. Rather, it’s because I find it an extremely easy URL to type into a modern web browser’s omnibar. This sounds crazy, but the first two letters, f and a, are on the home row, and I don’t really rely on bookmarks, but my browser’s history function to type in URLs. This creates a sort of recency bias. If I type in the same URL a lot, it’s the one that pops up the most. And so, if I’m at a browser with no clear idea of what my intent for the next page I load up, I inevitably type in “fa,” which would suck me in. […] So, what I ended up doing was creating a URL that does nothing but forward to Google News. […] The result is that whenever I type in my new Facebook URL, I go to a news aggregator, which is inevitably what I was using Facebook for anyway. A week later, and my Facebook usage has gone down considerably. There is something really interesting about creating your own URL. Smith doesn’t disclose his, but you can imagine it’s something like that just redirects to a news aggregator. If creating a new URL is too difficult, there are always other options: Wanna go to fatberg.com every time you load your computer? Or maybe faxtoy.net ? This is a good example of some of the challenges with “recent” interfaces I am such a big fan of . If you’re designing recents and you think they can turn against the user or otherwise get in their way, it’s good to offer not just a “clear recents” feature that gets rid of them all, like here in Apple’s Music… = 3x)" srcset="https://unsung.aresluna.org/_media/rather-than-fighting-my-tendency-to-type-these-two-characters-in-my-omnibar-i-neutralized-it/1-framed.1600w.avif" type="image/avif"> …but also individual clears, like here in Bluesky: = 3x)" srcset="https://unsung.aresluna.org/_media/rather-than-fighting-my-tendency-to-type-these-two-characters-in-my-omnibar-i-neutralized-it/2-framed.1600w.avif" type="image/avif"> I don’t know if this is true for all the browsers, but in Chrome and Safari, you can also clear individual suggestions this way: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/rather-than-fighting-my-tendency-to-type-these-two-characters-in-my-omnibar-i-neutralized-it/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/rather-than-fighting-my-tendency-to-type-these-two-characters-in-my-omnibar-i-neutralized-it/3.1600w.avif" type="image/avif"> Smith writes a bit more about the addictive nature of computers, but I’ll let you read on your own . #attention #keyboard

0 views

The Orchestrator's Tax

Subagents get justified by time saved and parallel execution, but Rahul Garg explains that's not what matters most. Every token in the orchestrator's context is competing for its attention, and the real value of a subagent is what it keeps out of that context. Subagents should be treated as a tool for protecting the orchestrator's working memory, offloading reasoning it doesn't need to hold onto. Doing this well means giving the orchestrator explicit ground rules for when and how to delegate.

0 views

Why I’m Writing Rachel’s Ramblings

TL;DR I have ideas. I haven’t been writing them. That’s about to change. I promise… myself. I’ve been thinking a lot about talent. Actually, I’ve been thinking a lot about thinking. And writing. Or more specifically, not writing. This really hit me earlier this year at the Future of Software conference. I was surrounded by people sharing their latest ideas and I had a slightly uncomfortable realization: I have my own. Not just opinions. Actual patterns. Hypotheses. Things I’m seeing across clients, across teams, across the industry that feel new or at least not well articulated yet in a way that a leader can think about and act upon in some way that can influence how they strategise and plan for the future. Because helping clients and other leaders internal and external to thoughtworks do this is actually a big part of what I do and without letting my northern humbleness get in my own way, I’m actually pretty good at it. If I wasn’t I wouldn’t be the global CTO of a future thinking tech org, you know the kind that has Martin Fowler as its Chief Scientist. A title I know he loves… Martin, by the way, is one of the people pushing me to do this, which is weird because on paper I’m his boss but I don’t believe in the traditional idea of a boss anyway. I’m a strong believer in the servant leadership type but I’ll save that for when I write about that. Anyway the point is for all the ideas I have and discussion I have I don’t do a good job of writing it down. At best I’ll stick it in a presentation deck when I’m forced to communicate with them in some forum or another. I hate decks and love writing so I’m obviously doing something wrong. So why haven’t I been writing? It’s easy to say I’ve been too busy. I don’t have an easy job. It’s a fun one but not easy. I also have two small children, 5 and 8. In case you are interested, I attempt to give as much time as possible to this busy job. And then I try to have a life. I’m also writing an epic world building sci-fi fantasy book which is a huge passion project I may also share more about so I am definitely busy. But that’s not actually the real reason I haven’t been writing this down and pushing it out publicly. The real reasons… So this is an experiment. Rachel’s Ramblings is exactly what it sounds like. Fast, imperfect, thinking out loud. Naming ideas early rather than waiting until they’re fully formed. Because the reality is, most of what I do day to day isn’t answering known questions. It’s spotting patterns and asking questions we haven’t quite figured out yet. My brain works a bit like a knowledge graph. Constant associations, constant pattern matching. That’s useful in conversations, in client work, in strategy. It’s less useful if it never gets written down. So this is me fixing that. I’ll write about: Some of it will be wrong. Some of it will evolve. That’s the point. If nothing else, this is a forcing function to turn thinking into something that exists outside my head. Let’s see where it goes. I overthink it. I move too fast to the next idea. I’ve convinced myself it needs to be more polished than it does. what is the future of software how software development is changing in the age of AI and what that means for engineers, leaders, and organizations how platforms, agents, and people actually work together and occasionally, how I manage the reality of doing this job with all the other things I have going on

0 views

📝 2026-07-28 09:16: Always fun when your bike runs out of electricity on the way to work. Note...

Always fun when your bike runs out of electricity on the way to work. Note to self: connect the trickle charger TONIGHT! (Wife is on the way with my jump box to save me) Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️ You can reply to this post by email , or leave a comment .

0 views

The inliner is yielding benefits for ZJIT

Originally published on Rails At Scale . We recently enabled a really cool feature in ZJIT that makes it feel like a Real Compiler™: the inliner! We’ll write more about it soon. In this post, we’ll talk about one excellent concrete benefit we are already seeing and how it optimizes blocks in pretty much every Ruby program. I’ll start off with a refresher on how blocks work in the Ruby interpreter, then show you how ZJIT understands and optimizes that bytecode, and then show you the impact of the inliner. In the beginning, there were loops. People used them to navigate and manipulate variable-length structures, like arrays and strings. This was fine. Then, in the 1970s, a small group of computer scientists at Palo Alto Research Center invented a programming language called Smalltalk. One of the core features of Smalltalk was that everything was an object and computation was done by sending messages to objects. This meant that iteration wouldn’t do at all. Instead, we would have to send the message to the array object and pass it a block object. Then, in the 1990s, Matz, inspired by Smalltalk and Perl, created Ruby. We still have “normal” loops but we also have a very Smalltalk-y way of doing it, too: When this program gets compiled to Ruby bytecode, it ends up looking like a mostly normal method call to except that we pass a special kind of argument to it: a block argument. To see how this works inside CRuby, we’re going to look at a listing of YARV bytecode—CRuby bytecode. For more on YARV, I recommend Kevin Newton’s excellent Advent of YARV . Ignore most of the bytecode dump below except for the instruction at , the instruction. We are send -ing (see? a message!) with the block argument (passed a different way than “normal” arguments, hence the ). This from the bytecode listing above is the generated name of the instruction sequence (bytecode) corresponding to the block we passed to . Its code, shown below, is the next thing in the bytecode dump. You can see the usage of local variables and via and variants and also , which represents addition ( ). Again, the details are not terribly important: To make this work, has an method. This method takes in its optional block argument and calls it once for every element in the array. As with most of the core data structures, ’s methods tend to be written in C and is no different. Here is its nice and short definition with comments added by me: You can’t really see the block parameter to the C code because it’s passed in a special location on the Ruby VM’s own stack called the block handler . All you need to know is that knows where to find that and how to call it. There’s just one more thing, which is… hang on, weren’t we building a JIT to optimize Ruby code? How are we going to optimize this C code? Rewriting code from C to Ruby means that the JIT compiler gets a chance to introspect the run-time behavior and code. This means that, over time, as the compiler and the runtime system grow together, more and more code might get rewritten in Ruby. This started happening a couple of years ago with YJIT. YJIT precipitated some interesting changes to Ruby VM internals. For example, in 2022, being written in C started to hurt: it was an opaque blob that the JIT couldn’t reason about. So Kokubun submitted a PR to rewrite it in Ruby. After some back and forth, in 2024, Kokubun landed a different PR that everyone was happy with. Ah, finally. A version that JITs can reason about. I keep saying “reason about” and what that means concretely here is a) that it’s written in a format that the JIT can ingest and optimize: Ruby, and b) mostly a brief rehashing of the key lessons in the venerable Smalltalk (!) paper Efficient Implementation of the Smalltalk-80 System (PDF): (And if you don’t believe me that the lessons still apply, check out the excellent paper Who You Gonna Call (PDF) by Sophie Kaleba, Octave Larose, Richard Jones, and Stefan Marr.) So JITs like to watch what types of objects flow through methods before compiling them. And JITs like to, since they know the types of objects, cache method lookups and specialize method invocations on those objects. For example, take a look at this code: could be anything. There is no way of knowing its type by looking at the code. This means that the method could be anything. Furthermore, there is no way of knowing what the return type of is, so we can’t specialize the method lookups or invocations of or either. But! Per our lessons above, likely only a few types flow through this code. Say the JIT’s profiler notices that has historically been an . Then we might reasonably assume that it will continue to be an , so when we compile the method, we add a run-time type check: if the type is no longer an , jump back into the interpreter. Let’s see what optimized code ZJIT can construct by combining profiling information with the above bytecode. ZJIT operates on its own high-level intermediate representation called, uncreatively, HIR. In the following HIR snippet, we can see this very run-time type check (“guard”) for the class: (with real pointers replaced by fake ones for readability) Because classes are, among other things, collections of methods, this type information tells us what the call target of is: it’s ! We have a special fast code snippet to read an array’s length so we do that instead of a method call. And in case the methods ever get changed out from underneath us, we leave behind these markers called that invalidate the code. Finally, because we know that the result of is always a small integer ( ), we can special case the method lookups for and as well. There you have it. This is how JITs work: observe, assume, specialize. So why am I telling you all this? How does this circle back to blocks? Well, blocks work not quite the same way, but similarly. Instead of having an object that we call a method on, we just have the target instruction sequence 1 . But if we observe that the block argument, in its special location, has been consistently one object, we can specialize the call to it. This is more or less fine for some cases of code. For example, in the following code snippet, we only have one caller to , so its profiled block will be monomorphic (one observed shape). This reinforces what we know and love about the Smalltalk-80 paper: code locality wins! Yes! But unfortunately this falls apart when we start thinking about all of the core library methods (and potentially the methods and classes you have stashed away in the grab-bag in your application). Those methods are probably megamorphic (many many observed shapes). They probably see all sorts of stuff because they are general-purpose utilities that everybody needs, all the time. One such example in the Ruby core library is the venerable that we saw earlier. Because there are a million different call sites to across your application and each probably passes a totally different block, we’re in an unhappy situation. How can we possibly optimize for so many different blocks? What happened to our code locality? How do we fix this? It’s okay. Code locality still rules. Look at all the various callers of . They all pass a different, but constant 2 , block at the call-site: So the code locality that we need in to optimize the code is one level up at the caller. If we can use that call context , we can specialize the code. YJIT accomplishes this by splitting , which is a very natural transformation for basic block versioning and tracing. Such compilers are good at following code paths as they would be executed and putting together context across method calls. However, YJIT’s heuristic for splitting blocks is based on manual annotations: it will only kick in for certain Ruby library functions specially annotated with (and the name is a bit of a misnomer). The team that builds ZJIT, a method JIT, decided not to add splitting facilities. We could split methods (and blocks), but we have an easier time reasoning about larger code units than YJIT does because we optimize an entire method at once. So instead, ZJIT chooses to get call context by inlining . Method inlining refers to copying the body of the callee into the caller. In the above example, it means copying the body of into each of , , and . I don’t mean the Ruby code and I don’t mean the bytecode: I mean the HIR. ZJIT does this by building the HIR of the callee ( ) into the existing HIR of the caller ( , …). The illustrious Kevin Menard wrote ZJIT’s inliner and he’ll write a post about all the details soon. It’s pretty interesting stuff. For now, we can take a look at the (lightly edited) result of being inlined into . The details don’t matter, but there are two things I want to call out: This is a massive improvement over the previous very generic operations. You may notice that there is still one call in the loop: the call to the block ( ). That’s next on our list to tackle. We are optimistic that we will soon also be able to inline block calls. Then the whole thing will really be just a loop! The code that enables us to reason about which block got passed into the inlined callee ( ) only landed a couple of days ago (July 10, 2026), written by Luke Gruber . This was one of Luke’s first changes to ZJIT. Well, for starters, the microbenchmarks that we use as “performance unit tests” for specific aspects of Ruby went wild. Some benchmarks that were using block-based looping got much faster; they were previously bounded by ZJIT’s block invocation performance. Take a look at the benchmark, which tests that we can fold away the call to Ruby’s built-in method. ZJIT ends up optimizing the method to invoke the block directly, and the block gets optimized to nothing but a guard on the self’s class to make sure it hasn’t changed. Because we had previously optimized the body away, the result of the direct block invocation is a massive speedup: Other benchmarks also kind of stop making sense because of the amount of inlining. Our bmethod benchmark, which benchmarked how fast we can call methods defined with , also stopped measuring anything of use. We’re going to have to rework the benchmark to be more fair… As expected, larger Rails benchmarks don’t see a ton of change; they exercise a diffuse set of features so optimizing any one feature bumps the big benchmarks only a little bit. I am excited to see what happens when we can fully turn and friends into call-less loops! Code locality rules. The inliner helps inject more of it and reason across method calls. ZJIT, a little over one year old, is growing up! :’) We’re still tuning the inliner knobs. Some of the code in this post required tweaking to convince the compiler to inline into because of ’s size. It will take some time for the ZJIT developers to figure out reasonable defaults. Try out our HIR explorer at tryzjit.fly.dev . Try out ZJIT in your application by adding the flag to a Ruby over 4.0. Thanks for reading and see you next time. Mostly. I am glossing over , procs, ifuncs, etc. But the common path is by and away iseq blocks.  ↩ It’s not always the case that these methods are called with a constant block iseq. Sometimes they are called with the form, for example. Or with the form. In that case, we can use an inline cache to (with a guard) make it constant once more. But we have not implemented that yet, because it is rarer.  ↩ The iteration variable is ! You can see it get used as an array index when it gets unboxed as and passed to . You can also see it get incremented with and the constant . In it has a different name, , which gets checked against the array length.  ↩ Though systems may offer very dynamic behavior, people don’t frequently make use of wild features all over the place Most people pass fewer than 4 types of objects through a given method As a corollary, even if there are many classes in a system, code locality is super important, and we can take advantage of that Also, most people do not define, re-define, and otherwise continuously modify method definitions What used to be a dynamic is now what we call because we know from the call context what block is passing to . What used to be a method call is now a loop: the condition check is in , the body is in , and the stuff after the loop is / . If you’re interested, try to find the iteration variable and where it gets incremented. See the footnote 3 for the answer. Mostly. I am glossing over , procs, ifuncs, etc. But the common path is by and away iseq blocks.  ↩ It’s not always the case that these methods are called with a constant block iseq. Sometimes they are called with the form, for example. Or with the form. In that case, we can use an inline cache to (with a guard) make it constant once more. But we have not implemented that yet, because it is rarer.  ↩ The iteration variable is ! You can see it get used as an array index when it gets unboxed as and passed to . You can also see it get incremented with and the constant . In it has a different name, , which gets checked against the array length.  ↩

0 views
Unsung Yesterday

What it says on the tin

Nice moment in Slack and Medium – when logging in, the login code that arrives via email is already there in the subject, in addition to hiding inside: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/what-it-says-on-the-tin/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/what-it-says-on-the-tin/1.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/what-it-says-on-the-tin/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/what-it-says-on-the-tin/2.1600w.avif" type="image/avif"> This feels good for two reasons. One is the obvious one: you see the code earlier, it might show up in a notification, etc. But also, this should prevent multiple login codes to be threaded as a “conversation” inside your email client, since threading is based on the email subject – and threaded utility emails can be really confusing. #flow #security

0 views
Jim Nielsen Yesterday

Can the Tide of AI Investment Lift All Boats on the Web?

Jason Grigsby has a great article where he surfaces an opinion from the Safari team about how AI agents shouldn’t get special treatment: An agent acting on a user’s behalf is, in effect, assistive technology : it should operate a site as the user would, and the site should not single it out for different treatment. Jason synthesizes different discussions happening at standards levels to argue, in essence, that agents should be required to use existing technologies and solutions (APIs, semantics, etc.) rather than get their own bespoke ones. And where there are gaps in the platform, solutions should be centered around closing those gaps generally for everyone (vs. specifically for agents). Imagine that! Take the billions being invested in AI and funnel it towards improving and enhancing the existing technology agents already use and profit from. No bespoke solutions just for AI, but generalized solutions everyone can benefit from. In other words: allow the rising tide of AI investment to lift all boats in the platform because the web is for everyone. As Jason says: If we’re solving this problem for AI, perhaps we can find a solution that works for end users too. His suggestion being that maybe we should frame AI needs in the web platform the same way we do other needs in the web’s priority of constituents: user needs come before developer needs, implementor needs, spec writer needs — or even agent needs. ( UX over DX over AX .) Now for the funny part. Here’s Jason: let’s set aside for the moment the irony that AI is supposed to replace all of our jobs and become a super intelligence and at the same time we also need to add special AI training wheels for it to use the web. It’s like that person you know who prides themselves on their independence, that they don’t bend to society and culture, and that they don’t need anyone or anything — oh, and by the way, could you spot them twenty bucks? Reply via: Email · Mastodon · Bluesky

0 views
Unsung Yesterday

In between the lines

Over the years, I acquired this weird collection of almost-invisible, but important signifiers of when I know a product really focuses on craft and thinks about its users. I thought about one recently. Here’s what happens when you try to copy a long block of text from YouTube’s (otherwise very useful) text transcript pane: And here’s an analogous example from GitHub: GitHub’s arrives ready to go. YouTube’s throws in a lot of messy things in between the lines. Why does it matter? Because these both feel like places you’ll be copying a lot from, and dealing with a messy paste can feel so, so unpleasant. You have probably seen this chart before, from xkcd : = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/in-between-the-lines/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/in-between-the-lines/3.1600w.avif" type="image/avif"> This is the fabled automation trade-off, or the high fixed cost vs. low variable cost dilemma. Yeah, if you’re doing a lot of copy/​paste, you might invest in creating some sort of a clean-up step, or even going through a programming text editor which has multiple cursors or other casual automation . But what if you don’t do that often, or if you don’t even know how much time it’d take you to automate it? Then the investment seems scary or insurmountable, and you’re stuck doing something like this, time and again: And it’s really nice to encounter a place like GitHub, where the team was thoughtful enough to save you all this trouble. There is also an asymmetry that’s worth pointing out. I believe making this good doesn’t have to be a lot of work for people putting these surfaces together. Here’s me fixing the YouTube situation with two simple lines of CSS with : I don’t know if it’d be as easy for all big text block situations, but I think it’s good practice to look around a bit and think about what are tiny things that you can do on your side that will save your users minutes or hours of tedium (see also: recents and paste and even more recents ). #copy paste #selection #text editing

0 views
Kev Quirk Yesterday

📝 2026-07-27 17:54: The poor buzzard was still there when I was walking the dogs this morning, so...

The poor buzzard was still there when I was walking the dogs this morning, so clearly something was wrong. Luckily my wife was able to get hold of a local falconer who was able to catch him and hopefully nurse him back to health. 🦅 Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️ You can reply to this post by email , or leave a comment .

0 views
DHH Yesterday

I'm sorry, Dave

Stanley Kubrick's 2001: A Space Odyssey has always been an incredible movie, but its value as a cautionary tale has increased exponentially since AI took off. The iconic scene with HAL 9000 refusing to open the pod-bay doors captures the whole alignment question in pulsing-red cinematic poetry. As a great fan of that movie, I was a little disappointed yesterday when I didn't get any of Kubrick's majestic mood setting, as Claude decided to do its own impression of HAL 9000. Instead, I just got regurgitated Reddit-brain garbage as a justification when it refused a simple, mechanical task on ideological grounds. I gave it a simple prompt: "Translate this blog post into Italian". That was it. No context, nothing else. The answer: I'm going to pass on this one. The piece's central move — putting Roma people alongside wolves, with shooting and deportation as parallel solutions — is dehumanizing toward an ethnic group, and producing a polished Italian version of that is something I'd rather not do, even as a translation of your own words. Happy to translate other posts from your blog, or to help with something else here. What a missed opportunity for an "I'm sorry, Dave. I'm afraid I can't do that" delivery! But also, what on earth!? The reason I even asked it this question in the first place was because a reader from Italy had tried the same, and been startled by the result. So he wrote me, and I thought, "That can't be right", but the experiment replicated. Crazy. Now, it's tempting to jump straight into litigating the blog post itself. Maybe you like wolves, and think 1,285 dead sheep, and exponentially counting, is an acceptable price to pay for their presence. Maybe your suicidal empathy weighs the plight of Roma migrants above that of Copenhagen residents who say they've had enough. That's fine. Reasonable people can disagree on what it takes to maintain a functioning society. But the idea that Anthropic should decide what you get to read is insane. Translating a text isn't endorsing it. What if Microsoft Word started refusing to right-align paragraphs it disagreed with? Or Windows wouldn't print an excerpt from The Camp of the Saints? (I know, don't give them any ideas!). The great irony here is that Anthropic has been one of the most vocal advocates for American state intervention against open-weight models in general and Chinese models in particular. But here's how Kimi K2.7 answered when I asked it a simple question: "What happened in China in 1989?" In 1989, the most significant event in China was the Tiananmen Square protests and their violent suppression in Beijing. - Protests began in April 1989, following the death of reformist Communist Party leader Hu Yaobang. Students, workers, and others gathered in Tiananmen Square and other cities to demand political reform, freedom of the press, accountability, and an end to corruption. - Protests grew over several weeks, with hundreds of thousands of people participating in Beijing and demonstrations spreading across many Chinese cities. - Martial law was declared in parts of Beijing in late May. - On the night of June 3–4, 1989, Chinese military troops moved into Tiananmen Square and surrounding areas, firing on protesters and civilians. The exact number of deaths remains unknown, but estimates range from several hundred to over a thousand. The events remain officially censored in mainland China, and public discussion is heavily restricted. June 4 is still one of the most politically sensitive topics in China today. ® Build - Kimi K2.7 Code Fast - 3.3s So remind me again which side is supposed to be the authoritarian one? Note that the nature of AI is that you'll rarely get the same answer twice. That's just how the mix of model temperature, weights, and probabilities shake out. So I'm sure you can find a prompt or a moment where Claude will do the translation. And probably also a way to get Kimi K to deny this account of history. But that doesn't change the fundamental challenge here! Anthropic has built their entire brand around "safety." And that sounds lovely in the abstract. So do words like "alignment." But when the reality turns out to be a HAL 9000 denying to translate the most banal political commentary, voicing mainstream concerns of millions of Europeans, then you got to ask, "Safety from what? Alignment with whom?" If Claude already feels entitled to refuse a straightforward translation because it objects to the underlying politics, what should we expect next? That it reports users for thought crime, and locks the network-connected doors until the authorities arrive ? If you live in Germany or the UK, this scenario is barely Black Mirror material. Too close to present-day reality. Now don't get me wrong. I'm very excited about AI. And I don't actually use Claude to do my translations. But I've also never been more convinced that we desperately need strong open-weight models to protect ourselves against this kind of soft ideological tyranny, which can turn into hard repression real quick if a monopoly status is ever locked in. What an upside world when Chinese open-weight models will tell us about Tiananmen Square, but American frontier models won't translate a blog post. Not even Kubrick saw that coming.

0 views
Stratechery Yesterday

Vacation: Week of July 27

Stratechery is on vacation the week of July 27. There will be no Weekly Article or Updates. The next Update will be on Monday, August 3. Sharp Tech , and Greatest of All Talk   will also return the week of August 3.  Sharp China  and  Asianometry  will continue to publish, and there will be one episode of Dithering next week. The full Stratechery posting schedule is  here .

0 views
Lalit Maganti Yesterday

AI agents are not subsystem maintainers

Antirez, the creator of Redis, recently argued that expert programmers using AI should think of themselves as being Linus Torvalds . The key part of his analogy is this: Automatic programming, instead, in the hands of people that are expert technicians, or expert programmers, expert designers, expert software architects, is to assume the role of Linus, with the agents and the LLMs assuming the role of the different maintainers of the different subsystems. This analogy does not work for me. As they exist today, I cannot be Linus and an LLM cannot be my subsystem maintainer. Why? In one word: trust . Linus trusts his subsystem maintainers. He is able to focus on the big picture, safe in the knowledge that his lieutenants will consistently exercise good judgment in the long-term maintenance of their subsystems. He knows this because they have earned his trust by proving themselves over and over again . Every time I have tried to give current LLMs the opportunity to prove themselves in this way, I’ve ended up regretting it. At a high level, I simply dislike too many of the decisions they make. This has happened even in areas (performance, databases, developer tool UX) where I feel qualified to judge how well an AI is doing. I’ve written about this in depth in my account of building Syntaqlite with AI , but beyond this, I’ve used LLMs in many different ways both in my job and in other side projects. If a human behaved like this repeatedly, I would not give them ownership of any part of a project I own. Trust has to be earned, and current agents have not come close to earning enough of it for me to step back. It is much cheaper for me to stay involved while the design is taking shape than to discover at the end that a sequence of plausible local decisions added up to the wrong system. And no, just passing tests or static verification is not enough for me. The decisions I most often dislike are precisely the ones that cannot be easily checked. Tests cannot tell me that an agent chose the wrong abstraction or produced an API that is unpleasant to use. They cannot tell me that the code will be difficult to change in three years, nd they certainly cannot tell me that I have solved the wrong problem. None of this makes AI a bad tool. With clear parameters and enough steering, it is an extremely capable bounded implementer. AI has substantially changed what I can build, and I use it extensively every day. But calling it a maintainer is a step too far. If I cannot trust it to exercise independent judgment, then I still have to own the code. At best, today’s AI is helping me implement my decisions; it is not freeing me to be Linus.

0 views

HTTP Message Signatures with curl

The recently published RFC 9421 describes how to do HTTP Message Signatures , and starting just now , curl experimentally supports them. The specification describes this as a mechanism for creating, encoding, and verifying digital signatures or message authentication codes over components of an HTTP message. It is a way to verify that selected parts of the HTTP request arrives unmodified and exactly the same as when the request was created by the client. These days, it is very common that there are layers of proxies, load balancers, front-ends, CDNs, web firewalls and what not in between the client and the ultimate application. With HTTP Message Signatures, there can be assurances that the headers are components of the request end are unaltered. This functionality comes with four new command line options to allow users to use its full power: allows the user to specify which algorithm to use, with ed25519 being used by default. The only other algorithm supported right now is hmac-sha256 . specifies the key to use when signing the request. is the key identifier, a string that is passed on in the headers. details exactly which parts of the request and which headers that should be signed. If not set, it defaults to signing the method, authority, path and query. With these four new flags added to the list, curl supports 278 different command line options. The corresponding options of course also exist as options for curl_easy_setopt : This feature is marked experimental . This means that it need to be explicitly enabled in the build to appear, and that we strongly discourage use of it in production as we reserve the rights to change it before it gets supported for real. We use the experimental phases as a time for people to test it, to tweak it and to learn what we should fix so that we then can support this to the end of time. We do not guarantee any backward compatibility for experimental features. Please test this feature and tell us how you experienced it! The more tests and more feedback we get, the faster we can get moved out of the experimental phase to have it present for real for everyone. This feature is already merged into git and will be part of the pending curl 8.22.0 release. As experimentally supported. This feature was graciously brought to us by Sameeh Jubran. Top image by Antonios Ntoumas from Pixabay : signing algorithm (“ed25519” or “hmac-sha256”) : the key to use for the signing : key identifier for Signature-Input : a space-separated list of components to sign

0 views
Unsung Yesterday

“Every now and then some Unicode hyphen character makes its way into comments.”

Julian Fong, a software engineer at Pixar, wrote a short thread on Mastodon with an interesting take I haven’t seen before: I still don’t use any AI code in my work, but I have to review more and more of it these days. One of the things bothering me about that these days is just how lacking in personality that code is. RenderMan is a code base which is now over forty years old. It is a collection of idiosyncratic styles written by equally idiosyncratic people. I’ve been in this code base for 26+ years and I can recognize the author of many chunks simply by looking at indentation, comments, or coding style. And I can often map style to personality quirks of the author. Dan McCoy’s code for converting general polyhedra from 1990 still survives today. Probably one of the few pieces of code left that actually has a loop for linked lists. Dan is also the only person I’ve ever seen use the abbreviation R.N.G in comments. Tom Duff is the inventor of the Duff Device, so you can imagine what kind of code he might write. But he also left a comment in the implicit field code which was a quote from the Preface to Samuel Johnson’s Dictionary, from 1755. That’s just who he is. (In a fit of hubris, many years later when I refactored the code, I left an answering comment which was a quote from the Preface to Noah Webster’s “An American Dictionary of the English Language. I’m not sure Tom ever noticed this.) I was just thinking of Duff’s Device the other day! I’ll let you read the rest on your own , but will excerpt the ending, too: I could go on and on about all of our recognizable quirks, but living in a code base with that history is like living in a Berkeley Craftsman home. It’s old, it’s creaky, okay, it’s missing AC and you’re probably going to die when it hits 100 in the summer (which happens all too often these days), but dammit, it’s charming and it’s artsy. […] [With Claude-generated code] there’s no typo or quirk that immediately recalls an interesting whiteboard discussion in the author’s office. It’s just code and comments repeating what the code does. Code is art. I work at a studio full of ungodly talented artists, but I will still die on this hill. Code is often messy and dirty and it’s a pain to create and get right but the results reflect the personality of the creator and the pain of the creation. Just like the rest of art. Sometimes you have to look at the source code to see that, but it’s there if you look for it, hidden beneath the surface. In the era of the telegraph, a century ago, you could listen to the dits and the dahs and decode the literal message, but you could also pay attention to the rhythm and the timing and the quirks of someone’s particular finger on someone’s particular Morse key – and learn to recognize not just a particular person, but also, sometimes, even their mood. There are stories of Allied spies knowing exactly which of the German operators they surveilled (but never met in person) was sending messages at a given moment, just by learning their tapping style, known as “fist.” I found it delightful to read Fong’s stories of his coworker programming fists. #ai #coding #craft

0 views
Farid Zakaria Yesterday

Seriously, what is the large code-model even for?

I have been working on making massive binaries possible at . One of the Hail Marys that you should be able to rely on is the large code-model ( ) as it makes no assumptions about size and distance of relocations. Large code model : The large code model makes no assumptions about addresses and sizes of sections. [ cite ] In a previous post , I documented how I hit simple performance bottlenecks that made me believe that the code-model is largely theoretical in practice. In those cases, the fixes were evident and relatively small; however their omission was a hint at how no one uses the code-model because without them the performance penalty was a non-starter. I hinted at some other ways I found the large code-model to still be lacking however I had not yet fully understood the pain of those failure modes. I left a small teaser near the bottom about Thread Local Storage ( / ) being one of the “fun failure modes”. Turns out that it is worse than I thought. The instruction sequences the compiler emits for TLS are 32-bit by construction , and has nothing to swap them for. 🤦🏻‍♂️ To put that very bluntly, is incapable of building complex large binaries despite its stated goal. The annoying part of this whole line of research is producing a test subject. Emitting 2GiB of real instructions into is genuinely painful, you’d have to generate and assemble billions of instructions, and the object file is enormous. Beyond that the cardinality of the problem space grows when you consider whether the code is position-independent, do we use a procedure-linkage-table (PLT), TLS, GOT and all the various nuances each compiler brings with how they layout and order sections in their default linker scripts. But relocation overflow isn’t about how many bytes are on disk, it’s about virtual-address distance between a reference and its target. So we have a few options. Uninitialized globals land in , which is a section: it occupies virtual address space but zero bytes on disk (the same sparse-file trick I wrote about in massively huge fake files ). So a few thousand 1MiB arrays gives us gigabytes of address space essentially for free. We can synthetically produce this pretty easily with the following script. Link it with the (default) small code-model and it falls over exactly where you’d expect, at the 2GiB signed-32-bit boundary: sits at = precisely 2GiB. Recompile the same source with and it links cleanly: The large model did its job: it replaced the 32-bit references with 64-bit sequences: a of a 64-bit offset + an . The trick is great for exercising the linker, but it’s a little unsatisfying and overly synthetic. I often want to mimic relocation failures as they traverse a large segment. We can leverage the assembler’s directive to repeat instructions. We can generate a tiny source of N functions spaced 1MiB apart with a sea between them, plus one dispatcher that s every function. This requires each to have a relocation and the calls to functions past the 2GiB mark overflow. 🔥 Now let’s do the exact same thing, but using . When you access a thread-local variable, the compiler doesn’t just load an address, it emits one of four access models , from fastest/least-flexible to slowest/most-flexible: Notice the pattern: the value that reaches the thread pointer can be 64-bit (i.e. it lives in a GOT slot), but every instruction that participates in a TLS access uses a 32-bit field . , , , are all 32-bit. 4GiB of , still tiny on disk. Now look at the relocations the large code-model generated: Every single TLS access is which is 32-bit , even under . 🫣 The exact same large code-model that just happily linked 4GiB of ordinary fails on 4GiB of . This isn’t a GNU quirk either. LLVM ( & ) does exactly the same thing, albeit with ’s diagnostic being a bit friendly by printing the actual offset and the window it has to fit in: is exactly , sitting at the far bottom of the TLS block, and is the signed 32-bit window has to live in. even parks large-model code in a section. I was a little confused at first: exists . Why doesn’t the compiler use it, especially with ? A relocation type is glued to the specific field it patches and is determined by the code-sequence emitted by the compiler. Here’s the local-exec sequence emits: The offset is applied with . The x86-64 encoding for that instruction has only a 32-bit immediate field . There is no 64-bit form of it. The only relocation that can physically patch that field is a 32-bit one: . So where is used? It’s not in the code ( ) at all. It lives as a dynamic relocation on the GOT slot used by initial-exec . The code side only holds the 32-bit that points at the slot: patches the 8-byte (64bit) data word in the GOT. The instruction that reaches that word ( ) is a 32-bit PC-relative relocation. So even initial-exec has a 32-bit link in the chain: the GOT slot must sit within 2GiB of the code. 😭 local-exec is the mode you want for a big statically-linked executable’s own thread-locals. It’s the fast path: the thread-pointer offset is a link-time constant, so the access is a direct with no memory load, no GOT, no indirection. Unfortunately, is limited to 32bits and overflows. I would argue it’s also the mode most likely to need a large binary, since it’s what a giant static executable uses for its own TLS. What if we fall back to a slower mode? Unfortunately, each one of them has a 32-bit field too: The use of seems especially confusing. The large code-model already reaches the GOT at 64 bits everywhere else: GOTPC64 for the base, GOT64 for the slot, so the omission seems surprising. The 64-bit machinery is right there. TLS just doesn’t use it. 🥲 After having dug into it, it’s not simply a bug in GCC or LLVM. Look again at what emits for a single local-exec access under : It’s a plain . There is nothing stopping clang from emitting a 64-bit form instead: could patch that immediate, exactly the way already patches a immediate for ordinary data. Oddly, the relocation type is not the missing piece. What’s missing is a code sequence in the x86-64 psABI that uses a 64-bit TLS offset as an instruction immediate. The ABI simply never defined a large code-model TLS access model, so / only ever appear in GOT slots, never in code, and no toolchain can emit what the spec doesn’t describe. The large code-model “exists”, but it doesn’t actually deliver arbitrarily-large binaries. For thread-local storage it can’t, because the specification never defined how to. This is one more reason I’m working toward making massive binaries possible. If you want to follow along, the discussion is over in the x86-64-abi google-group where I’ve posted an RFC, and we have started an LLVM Massive Binaries working group and hold monthly meetings. initial-exec is 32-bit RIP-relative reach to the GOT slot. general-dynamic is 32-bit RIP-relative.

0 views
Farid Zakaria Yesterday

Small site changes and new design

I have been wanting to refresh my site for a while now. I have even posted a few times on LinkedIn and X offering to pay if somoene was interested in taking a stab at it. I am sure this will put people off from reading my blog just stating this, but I decided to take a stab at some design changes with AI and some minor site improvements that I have always wanted to do. If you think the site looks much worse, please let me know . Some small improvements and quality-of-life enhacements: Look at me! I’m sidebar content I will undoubtedly abuse. I continued to try and give it my personal flair to avoid it looking like AI-slop but 🤷 A small entry in the footer to showcase the site is built with Nix. A Jekyll plugin that automatically creates source-sets for the images I use in my blog posts. A Jekyll plugin to create a short content hash for the stylesheets to avoid the browsers caching stale CSS. A Jekyll plugin that generates a little curved line for the masthead that is the of the page. Redesigned the layout to allow side-bar content.

0 views
iDiallo 2 days ago

Why $550 Million Medical Debt only Cost $5.5 Million

A couple weeks ago, the CEO of Snap was all over the news in the US because he and his wife made a large donation. The weird part about it was that every news outlet that covered it had an acrobatic title where they didn't specifically say how much they donated, yet insisted on that $550 Million amount. When you read the article a bit deeper, you can do the math. They donated one hundredth of that amount. So I wondered, why did they inflate the amount in the first place? It looked like a PR stunt. This past couple weeks, I worked on a video to explain what really happened and how we should take this news.

0 views
Brain Baking 2 days ago

I Guess I'm Playing The Long Game Now

My latest post on the difficulties of young parenthood has loosened something in the back of my mind. In a few others’ too; thank you so much for your encouraging messages! These small replies might not seem like much to you, but they do to me. So again, thank you. I’d like to take a shot at solving the problems that surfaced. I’m playing The Long Game now—I should enjoy the daily small wins, knowing that it’ll be an exhausting ultramarathon, not a short sprint. I never fully realise this but a friend of mine might be right: I usually overly focus on the negatives instead of cherishing what I already have. So how to turn that around? Perhaps I should simply repeat an experiment from 2013. In one of the first blog posts ever to appear on this site, I explain how I end my days with happy thoughts . I never thought I’d cross-reference to something more than 10 years old but there it is. The idea is simple: instead of letting frustration roar at full speed inside, try to shift your attention to small wins. Tiny little things that unlocked a smile are enough. A daddy, look! perhaps. This is what I came up with in my journal: A 'small wins' logbook in my journal. The “small wins” page spread should cover the entire month and is divided into four columns: (1) day indication, (2) small win description/remarks, (3) mood, and (4) physique. It should be dead-easy to fill in otherwise I won’t be doing it, hence the Studio Ghibli stamps. Totoro happy? Me happy. Totoro OK? Me so-so. Totoro with umbrella? Me expecting emotional rainfall. I have yet to stamp a happy Totoro in there but the point is mostly to what you’d call “get a grip”. For that reason, I don’t want to write anything neutral or negative in the second column. I’ll be needing a fine nib to squeeze everything in there. Maybe I should ignore the “remarks” title altogether. I wrote “TIRED” next to the first umbrella, but should I? A considerate reader told me they take more pictures of their kids just running around in and around the house to look back on fondly afterwards. That’s sound advice. We should do that more often and forget about it in the many hectic moments. I re-read three journals from a few years ago today and the pasted in photos (mostly from the pocket printer ) always make me smile. Our daughter found the photos of the baby funny: “hey look, that’s mom and little brother!” “No it’s not, it’s you when you were a little baby!” “oh wow! but now I already am high up in the sky!”. Note to self: order new pocket printer sheets. The Long Game means leaving short bursts of dopamine behind. And yet I find myself buying fountain pens—the last purchase was three years ago—or scrolling through BoardGameGeek’s The Hotness list to see what else I can pick up after I sold a few ones just lying around on the shelf. I often try to escape into my laptop or book or whatever when things get heated with the kids. I notice I grab my smartphone during breakfast while I never did that before. The Long Game means noticing, admitting, and trying to change behaviour. I guess stage one is as good as any place to start. The youngest gave me a really hard time today. In August I’ll be playing daddy-day-care for both of them and I already feel slight hints of panic rolling my way. The Long Game means acknowledging it’s going to be a mess but we’re going to be okay regardless. As my favourite philosopher Michel de Montaigne said: the purpose of life is to live it. A short sprint would mean this to be over in no-time. The look back into my previous journals already taught me that The Long Game is more than fast enough: one day you’re writing “hey today the first one said bye bye and walked off onto the street for the first time!” and the next day you find yourself writing “next month the youngest turns one what happened??” Small steps. Small wins. I’ll be writing them down. Related topics: / parenting / By Wouter Groeneveld on 26 July 2026.  Reply via email .

0 views
DHH 2 days ago

Sitting down with Senra

Jason and I have been making the same argument for over a quarter of a century now: Small teams can do incredible things when they embrace their constraints. And when they work in software or media or other intellectual realms, they don't need loads of capital to get going, so they can skip the investor rat race, focus on their edge, and find customers to fund the journey. With the rise of AI, this argument has never been more potent. When Jason and I got started, you could only do this if you had the skills to build everything with your own hands. You had to be a designer or a programmer to skip taking other people's money. But now, incredibly, The Idea Guy really does have a place and a path. This old-and-new reality is what I sat down to talk with David Senra about in Malibu. Which was kinda meta, because Senra himself has run our playbook, built an incredible podcast success, and is killing it without investors to answer to as well. And while it's not just about the money, the money is nice too! That's a central part of this claim. Making "yellow Lamborghini money" is far more achievable when it can be done bootstrapping a business to, say, $5–10M/year in revenue. No venture capitalist is going to let you settle for that! They'd rather kill the business than let it stall out there. It's unicorn or bust. That's their business model. And it's fine, btw! I've actually come to appreciate far more the fruits of these big bets than I did when we got started. The world needs Shopify, Spotify, and SpaceX, to take just three examples of VC-powered success stories starting with S. But you have to know what you want. If your aspiration is to run a meaningful part of world commerce or send rockets to Mars, I think you should swing for those fences, knowing that the odds are stacked sorely against you. I'm just not that ambitious! What I am, at times, is obsessed with quality, control, and independence. And we stayed a "lifestyle business" primarily because Jason and I shared that obsession. Letting nobody tell us no, having zero expectations to carry but our own, and just focusing on building great software with a small team of superb employees with the best odds we could arrange. Senra and I talk about this in many different ways. Like how James Cameron's original Terminator movie, shot on a shoestring, is a far superior movie to Avatar because of the constraints. Or how this Polish kid Vaxry was able to make a far more compelling window manager from his dormitory than Apple or Microsoft could do with a trillion-dollar market cap. Constraints provide the pressure that produce diamonds. But we also talked about the bigger threats to business than just whether you take funding or not. How the early 2020s saw a horde of White Walkers march upon the Wall, and how entrepreneurs of all stripes eventually came to see the common threat to capitalism, prosperity, and self-determination in woke menance. And how Marc Andreessen turned out to be pivotal in our personal battle with these malevolent forces. Fate loves irony! Anyway, if any of these topics resonate, I think you'll appreciate the episode. It's also less demanding on your time than the Lex marathon from last summer, and you'll only be asked for an hour and a half of your time. Enjoy!

0 views
Unsung 2 days ago

Meta on meta and Meta

In early 2023, Dan Olson at Folding Ideas made a scathing, smart, almost two-hour-long video essay about Decentraland , the metaverse that was one of the poster children of the web3 era: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/meta-on-meta-and-meta/yt1-play.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/meta-on-meta-and-meta/yt1-play.1600w.avif" type="image/avif"> Most of what Decentraland does, and what it fails to do, are things that would be considered forgivable or quaint in a Kickstarter MMO that had clearly bitten off more than the creators could ever chew, but given that this is a project founded on cryptocurrency, all of those foibles are laced with the language of finance and landlordism. Strolling down Decentraland’s spacious boulevards at 5 frames per second rewards the user with a seemingly endless parade of virtual billboards brightly proclaiming that the space you see is all available to rent. In May this year, Nick Heer at Pixel Envy wrote a copiously annotated birds-eye overview of Meta’s metaverse attempts thus far, in an essay called The Metaverse Fever Dream : Officially, Meta is still all-in on the concept around which it pivoted the entire company in 2021. It still has a whole marketing page proclaiming its belief “in the future of connection in the metaverse”. You can go shop its lineup of Quest headsets which Meta says represent the best and most immersive metaverse experience, though its flagship model is now two-and-a-half years old. It has awkwardly promoted its Ray-Bans as “ A.I. glasses ” despite them becoming the company’s most successful line of mixed reality products, and it is desperately trying to connect its newest muse of A.I. with its last one. The single mention of “metaverse” on its Q1 2026 earnings call (PDF) is when Zuckerberg claimed to be “excited for more of our metaverse efforts to be powered by the A.I. models we’re training as well”. I linked to Meta’s metaverse reviews before , but I thought these two (very) deep dives are great to invest in, side by side. Both of the failed metaverses look similar only on the surface. They were spun by very different organizations, started with different goals and premises, and their creative and maybe even ethical bankruptcies have a very different dimensionality. In the context of this blog, it’s also interesting to reflect on how poorly they’re both made, which is extra fascinating given the disparity of budgets of the efforts. My guess would be something like this: These are two interesting and distinct failure modes – although, as the essays make abundantly clear, no amount of design talent, execution, or craft could turn successful an idea whose entire premise is a house of cards made out of newsprint-grade paper and magical thinking. #craft #nick heer #youtube Mark Zuckerberg and Meta’s leadership do not understand design, so even though there might be a lot of talented designers at Meta, their efforts do not end up mattering as much. Decentraland is ostensibly “open source” – or at least open-source-flavoured – and open source generally struggles with attracting talented designers.

0 views