Latest Posts (20 found)
Unsung Today

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

📝 2026-07-26 10:44: Taking the dogs for a walk with our oldest and we came across this beautiful...

Taking the dogs for a walk with our oldest and we came across this beautiful buzzard just sat there. Wasn't bothered by us really. I just hope it doesn't have a bust wing or anything. 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

A GTK4 ssh-askpass in Zig

I run hardened Gentoo on my laptop, and most of the time I never touch because I’m using keys for most of the systems. There is one class of situation where I do need it, though, which is when a program wants an SSH key passphrase for a regular ED25519 key, but has no terminal to read it from. The usual case is , or the toolchain in general, fetching a private module over SSH during a build that runs without a TTY. OpenSSH can’t prompt on a pipe, so it runs whatever points at and puts the passphrase prompt in a window instead. For years I had nothing installed for that and had to work around these scenarios. The main reason for that is what Gentoo ’s Portage offers: Each of these has at least one inconvenience I didn’t feel like putting up with. My system runs with the global USE flag, so anything that needs X11 is out before I look any further. Of the five, is the only one with no X11 dependency whatsoever, which should have made it the obvious pick, but the trouble is everything else that comes with it. As a Sway user , I did not want a full KDE stack on the machine just to type the occasional passphrase, and that is what a install pulls in: is next, and it needs outright. On top of that it pulls in a few KDE framework packages and a Qt built with support, which collides with the already on my system that was compiled , so Portage stops on a slot conflict: needs as well, this time by way of GTK2 and a Cairo built with support: is X11 by name, so no surprise there, and it also needs the old imake build system, namely and , to compile at all: That left . At first glance it looked like the one option that needed no at all, but that turned out to be wrong. It does need X11 , and the ebuild appears to be broken about it. The build calls and the source includes , an -only GDK header, so on a system compiled without it fails to build: This is where I gave up on the packaged options. Even setting the X11 question aside, every one of these uses GTK2 or GTK3 at most. However, it just so happened that I had wanted to build something with GTK4 for a long time, so instead of patching one of the existing implementations, which are mostly C anyway, I wrote my own with Zig 0.16 and GTK4 , and called it ssh-askpass-zigtk . The reason the GTK helpers break on my system is the headers. The standard way of calling GTK includes the GTK4 headers, which pull in GDK , and GDK still ships on most installs, so an X11 header comes in whether you want it or not. Zig ’s , the obvious way to call a C library, would do the same, because it pulls in exactly those headers. So doesn’t anything. declares the thirty-odd GTK and GLib functions the program calls by hand, as plain prototypes: Nothing in that file names a symbol from or , so the compiler never sees an header, and the binary builds and runs against a GTK4 that was compiled without X11 . The one -adjacent value it needs, the Escape keysym, is hardcoded as rather than pulled from . GTK is built on GObject , which does single inheritance by putting the parent struct as the first member of the child, so a window, a box, a label, a password entry and a button are all layout-compatible with a at the ABI boundary. On the Zig side one type stands in for all of them, and every widget function takes and returns the same , without a hierarchy of wrapper types to model something the C ABI already flattens. The parts that don’t touch GTK , the mapping of to a dialog type and the parsing of the variables, are in with unit tests, so they run under with no display and no GTK at all. Recoloring goes through a small CSS provider, since GTK4 removed and . Because the bindings are hand-written externs and no GTK headers enter the build, Zig can cross-compile the binary for any Linux architecture without a GTK4 toolchain for that target. The only thing missing at link time is the GTK4 shared library itself, and covers that, as it builds a tiny stub whose exported symbols are all no-ops, links the executable against that, and lets the target’s real GTK4 resolve at runtime instead. The release workflow uses this to produce binaries for , , , , , , and from one machine, none of which has GTK4 installed for the other seven. Note: doesn’t grab the keyboard as other askpass implementations normally would. The GTK3 helper calls so another client can’t read the passphrase as you type it, but from what I see, GTK4 dropped that interface and I believe that Wayland doesn’t let a client grab the keyboard at all, so there is no portable way to do it without X11 . Hence the and variables also have no effect. The code is on tty.fail and mirrored to GitHub , where each tagged release ships prebuilt Linux binaries per architecture. To use it, put the binary somewhere on your and point at it. For a terminal that means two lines in or your shell’s startup file (e.g. for my fellow Zsh users ): , from OpenSSH 8.4 onward, tells OpenSSH to use the dialog even when a terminal is available, as long as a graphical session is present. On a systemd user session, the same two variables go in as plain lines with an absolute path, since that file neither expands nor runs a shell. Log out and back in, and the next , pull or that needs a passphrase without a terminal gets the dialog.

0 views

Thoughts on Snow Country by Yasunari Kawabata

This morning I finished reading Snow Country by Japanese writer Yasunari Kawabata . The book was suggested to me by my cousin who, after our most recent dinner together where we talked at length about books, sent me a list of a dozen titles for me to read. When it comes to books that are suggested to me, I like to know as little as possible about them: I don’t research the author, I don’t read the blurb, I don’t seek reviews. I like to think that if someone suggested a book to me there might be a good reason for it and so I simply pick it up and read it. And that’s exactly what happened with this book. At the beginning of July I ordered 3 books from the list provided by my cousin: Neuromancer by William Gibson (already finished it), Kokoro by Natsume Soseki (started reading it today and I’m enjoying it a lot), and Snow Country. This morning, after I finished it, E. asked me if I liked it. And as I said to her, I’m not sure I know how to answer that question. It happens a lot with books. And if you were to ask me to describe this one to you, I wouldn’t know where to start. It’s a snapshot of life, suspended in time, left there hanging without what most people would consider a proper conclusion. But at the same time it felt complete, as if nothing more needed to be added. I don’t know if I enjoyed the book, or if I liked it, but I can for sure say that it left me with something , and that’s more than enough for me. Thank you for keeping RSS alive. You're awesome. Connect via email :: Sign my guestbook :: Support for 1$/month

0 views
@hannahilea Yesterday

Birduino: A card-triggered audio player for [learning] the birds

Tap a bird card to the NFC reader to hear one of its calls, identify birds from their vocalizations, and up your bird game!

0 views
neilzone Yesterday

Driving to London for the first time in years

Today, for the first time in years - probably 20 or so - I drove to London. I didn’t really want to drive to London, and it is daft that it was even a credible option. I’d much prefer to take public transport and, when I go to London for work, I do. Thankfully, there is a reasonable if not brilliant train service from Newbury to Paddington. Time-wise, there was not a massive difference between driving from Newbury to Westfield, and then taking the tube, and taking the train from Newbury and then taking the tube. Not much in it at all, assuming that everything is running correctly. No traffic jams, leaves on the line etc. The difference was in price. There were five of us travelling today - Sandra and me, and a friend with two children. The train fare alone, from the National Rail website, was going to be over £110, including a significant discount for travelling together (the “GroupSave” discount). There might have been a cheaper configuration of tickets, but this is what the National Rail website offered. I am not even sure if this covered the London Underground element or not. Instead, it cost about £10 in electricity for the car, £12 to park at Westfield, and then ~£30 on for the London Underground. So just over £50, plus some wear and tear to the car. And, of course, the initial outlay of buying and maintaining a car. Other than the last few miles to / from Westfield, the journey was easy. It was quiet (especially on the way back, when everyone else had a nap), comfortable, and cool. I still prefer the train, as I do enjoy being able to work or read my book, and when I normally travel for work I take my bike so I don’t need to deal with the underground either. I don’t really want to drive to London, but it certainly made financial sense today.

0 views
Unsung Yesterday

A time machine in Logic Pro

A nice moment in Logic Pro, a music app. Like with most such apps, you can press R to record you playing an instrument. However, if you forgot to play record, or were just goofing around and stumbled upon something wonderful, you can press ⇧R and the recording will appear anyway, as if you had a time machine. This is a quick TikTok video showing it in action: = 3x)" srcset="https://unsung.aresluna.org/_media/a-time-machine-in-logic-pro/tt1-play.1600w.avif" type="image/avif"> The feature is called Flashback Capture. Of course, just like with undo send , this is no magic. The app is always recording the events quietly, and then offers you to make them “real” if you want. I dug around and found a support document that offers a rare view into the mechanics of this feature, which are slightly more sophisticated than I imagined: When playback is stopped, Flashback Capture creates a separate region containing all the MIDI events received since the last playback. However, after a pause of 20 seconds between incoming MIDI events, those initial MIDI events before the pause are discarded. If when playback is stopped, you perform some MIDI events and then pause for 1.5 bars or longer, those initial notes aren’t included in the visible part of your region. If you do want those MIDI events to be included in the created region, you can drag the left region boundary to expose them. What it seems to say is: This seems like a good and thoughtful feature that prevents data loss, a sort of magical “reverse redo.” (Thank you to Chris Krycho for telling me about this feature, which is apparently also available in other music apps, e.g. Cubase and Dorico.) #above and beyond #details #errors #preview #undo If there is a longer pause within your play, the notes are still recovered, but hidden. You can always drag to reveal them, but in effect, only the most recent of your notes are immediately visible – a nice touch. (The moment you start dragging, it also shows you a quick preview of where you’re going, which is also thoughtful!) If the pause is 20 seconds or more, all the notes before that pause are no longer preserved – presumably to prevent too much wasted data in your file. However, after you stop playing, you have infinite time to invoke Flashback Capture and recover the notes.

0 views
Unsung Yesterday

“A vicious circle of incompatibility”

A fun 16-minute video from PortalRunner with this premise: This is an image file, containing a picture of my cat. But if I rename it to .MP4, it becomes a video file – also of my cat. If rename to .PDF, it becomes a text document containing the script for this video. It can also be a valid webpage, a .ZIP archive, or a PowerPoint presentation, all by simply changing the name. This kind of file is sometimes called a “polyglot” (although, usually that term refers to code that works in multiple programming languages). = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/a-vicious-circle-of-incompatibility/yt1-play.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/a-vicious-circle-of-incompatibility/yt1-play.1600w.avif" type="image/avif"> This kind of a file is not something that you will realistically need, but it’s a fun look into various approaches to headers and structures of file formats – something we don’t usually get to think about a lot. Buried inside the video is also an interesting digression: is the file extension just a method of delivering the file to the right application? If I rename .jpeg to .gif, and both are routed to Pixelmator, should Pixelmator do its best to detect it’s a JPEG file under the hood, or fail with a “this doesn’t look like a GIF file” message? The web has a similar challenge in the form of MIME sniffing – “MIME” is sort of the web’s equivalent of extensions, “sniffing” means detecting the file from its contents alone, ignoring everything else – and that had some security considerations, as it allowed bad actors to sneak in some malicious code under the guise of something more innocuous… basically what the video is doing for fun, but now weaponized. This is all pretty technical for this blog, but inside the Wikipedia entry for MIME sniffing is this passage that caught my attention: [MIME sniffing is still used by some browsers. However,] by making sites which do not correctly assign MIME types to content appear to work correctly in those browsers, it fails to encourage the correct labeling of material, which in turn makes content sniffing necessary for these sites to work, creating a vicious circle of incompatibility with web standards and security best practices. Decades before MIME sniffing, Jon Postel captured the essence of that line of thinking by coining Postel’s Law – “be conservative in what you send, be liberal in what you accept” – but as enticing as it is, that has challenges similar to the above quote: A flaw can become entrenched as a de facto standard. Any implementation of the protocol is required to replicate the aberrant behavior, or it is not interoperable. […] Ensuring interoperability in this environment is often referred to as aiming to be ” bug-for-bug compatible ”. While Postel’s Law was about data flowing in and out of computer systems, the premise is to me a more evergreen design question, applicable to so many other things. Feeling “liberal in what you accept” can feel helpful, but can teach users bad habits and have bigger consequences. For any project where this applies, it’s worth asking: should we go out of our way to help the user even if they mess up, or should we be more rigid and teach them to follow the rules more strictly, as it will benefit them in the future? The Command Line Interface Guidelines I linked to before had a great example of that: You can ask if they want to run the suggested command, but don’t force it on them. For example: Rather than suggesting the corrected syntax, you might be tempted to just run it for them, as if they’d typed it right in the first place. Sometimes this is the right thing to do, but not always. Firstly, invalid input doesn’t necessarily imply a simple typo—it can often mean the user has made a logical mistake, or misused a shell variable. Assuming what they meant can be dangerous, especially if the resulting action modifies state. Secondly, be aware that if you change what the user typed, they won’t learn the correct syntax. In effect, you’re ruling that the way they typed it is valid and correct, and you’re committing to supporting that indefinitely. Be intentional in making that decision, and document both syntaxes. #encoding #errors #youtube

0 views

Au-delà du chatbot : un harnais pédagogique

Cet article a aussi été publié sur mon Substack . Dans le contexte de la révolution en cours dans le monde de l’IA, on peut parfois avoir l’impression que les néologismes apparaissent de manière trop rapide et désordonnée, et qu’il est difficile de suivre leur évolution. Le fait que ces néologismes apparaissent tout d’abord en anglais n’aide pas, car la plupart n’obtiennent jamais de traductions satisfaisantes ou officielles, et on est donc condamnés, en tant que francophones, à les utiliser tels quels dans nos phrases.

0 views
Lalit Maganti Yesterday

How I Find Problems to Solve as a Staff Engineer

Note: this post was revised after publishing for increased clarity, based on reader feedback . “How do you find problems worth working on?” a senior engineer I mentor asked me recently. He’s trying to make the jump to staff engineer and realized that the role isn’t just about doing the work he’s assigned. He also needs to get involved in figuring out what his team and org should be building. Someone else had suggested blocking out time in his calendar to think about the bigger picture. He’d tried that, but hadn’t found it productive, so he asked if I had any alternatives. I told him I rarely find good problems by staring at a blank page and trying to “think strategically.” Instead, I act like a sponge. I listen to the stream of day-to-day noise, absorb the problems people are having and let them sit in the back of my mind. Over time, some fade away while connections begin to appear between others that initially seemed unrelated. Eventually, I start to see what’s really slowing people down and what my team or I can do about it. I’ve worked with many engineers who’ve never really tried this. They wait for managers or leads to identify opportunities, then demonstrate their value by solving the hardest assigned problems. That can absolutely lead to promotion. But the projects that have made the biggest impression in my career were the ones where I found and solved an important problem my leaders did not yet realize existed. One caveat: my experience comes mainly from working on infrastructure and developer tools at large companies, on teams where engineers have a lot of bottom-up autonomy to influence their roadmaps. In a more top-down environment, there may simply be less room to work this way. People love talking about the problems they are facing: in meetings, chat threads, presentations and email. They explain why their work is hard, complain about what slows them down and describe what they wish they could do. When something overlaps with my area, I start pulling on the thread. I might ask, “If X existed, would it solve your problem?” or point them at an existing feature in a product I own and ask how much of their use case it covers. Users often ask for a particular solution instead of explaining their root issue. Rather than taking the request at face value, I keep digging until I understand what they are trying to accomplish and why existing products do not work for them. As a natural introvert, this sort of ambient listening works particularly well for me. I don’t need to fill my calendar with speculative meetings just to find ideas; there is already an enormous amount of useful information flowing around me during a normal week. When a problem seems worth exploring, though, I become more active; I need to see how it affects the team’s day-to-day work. I’ll sit with them as they walk me through their workflows and the bugs they’re investigating. When I can, I’ll try working through some of those bugs myself. Seeing the problem firsthand makes it easier to separate what the team actually needs from the solution they asked for. I also seek out people who see more of the organization than I do: those who own critical systems, work across several teams or have particularly deep insight into the work downstream of my team. I’ll arrange a 1:1 or coffee chat and ask about interesting problems they’ve come across. They may have already seen the same issue in several places and started connecting the dots, giving me a head start on patterns I might otherwise have taken much longer to notice. Several times, I’ve been burned by moving too fast. I became excited by a request from a vocal team, built the feature and watched them barely use it. Their priorities had changed, or the request had come from a one-off investigation that no longer mattered. How eager a team was in that moment wasn’t the same as how important the feature was relative to everything else my product needed to support. By hyperfocusing on their request, I lost sight of the bigger picture. That taught me to let potential problems pile up. Listening the way I do leaves me with far more of them than I could possibly solve, and not all deserve action. Most don’t need to turn into projects the first time I hear about them; waiting can be a superpower. Waiting means the same problem might pop up independently in different teams, making it a higher priority to solve. Or problems that look different on the surface might turn out to have the same shape, so I can address several use cases in one shot. Or, as I’ve learned painfully, the requesting team didn’t even care that much in the first place. Instead, I make a mental note and revisit the problem if it comes up again. Other engineers I know write this sort of thing down more systematically. The mechanism is a personal choice: everyone has to figure out what works for them. What matters is keeping unresolved problems around long enough for more evidence to accumulate. Waiting helps me collect evidence, but that alone doesn’t tell me what to build. I still need to work out whether the problems I’ve retained are genuinely related and what, if anything, could address them together. Perfetto, the performance debugging tool I work on, is a good example. It displays recordings of system activity on a timeline made up of rows called “tracks.” Over a couple of years, teams kept asking for small, specific additions to the UI. One wanted a command to keep their preferred tracks pinned to the top of the screen; the next team wanted the same, but for a completely different set of tracks. Others wanted Perfetto to open already zoomed in on a particular part of a recording, or to show a custom aggregation tuned to what they cared about. A few had stopped waiting for us and built elaborate workarounds with bookmarklets. 1 By the time enough of these had piled up, my head was the usual tangle: the requests themselves, the constraints on each and a handful of half-formed solutions. I’ve learned not to force a solution by just sitting at a desk and thinking. Instead, my best untangling happens on long, aimless walks around London, where connections come more easily when I’m not trying to force them. What I eventually realized was that none of these teams really wanted the specific feature they’d asked for. Each wanted to personalize Perfetto for their own workflow without imposing their choices on everyone else. The underlying need wasn’t any one feature but rather the ability to extend the UI. When a connection like that finally clicks, it’s one of the best feelings in the job: several awkward requests collapse into a single idea, and possibilities open up that none of them hinted at on their own. That feeling, though, is exactly when I have to be careful, because a common shape is only a hypothesis and elegance is not evidence. When it happened with extending the UI it turned out to be real, but I’ve been fooled before. In another recent case I was convinced that building a transparent caching system for querying Perfetto traces would solve issues with sharing large traces and repeated queries. It was only as I wrote the RFC and built a prototype that I realized the elegance was a lie: the two problems wanted genuinely different solutions. I reluctantly split the design in two, both halves of which have since shipped. 2 You’d think this would be the moment I start building, but it usually isn’t. How far I go depends on how sure I am that the idea works and that people actually want it. If something is useful and low-risk enough, I act straight away: I send the change and let my manager know. When I’m unsure whether an idea will work or how much effort it will take, I build a throwaway prototype instead; it exposes the failure points and gives me something concrete for others to react to. And when an idea is big but I’m convinced by it, I commit to the full effort: weeks or months of work and the hard yards of building support across other engineers and teams. Through all of it, I’m not only trying to convince other people; I’m also trying to convince myself. Sometimes the honest answer is to stop: if people don’t see the value I do, or we hit a major technical wall, I’d rather drop the idea now than build something no one uses or that becomes a maintenance nightmare. And sometimes it holds up but the timing is wrong, so I park it, ready to spring into action the day it becomes an org priority. When an idea does hold up, I don’t necessarily need to be the person who builds it. I might implement it, someone else on my team might, or it might change what the org focuses on. Finding and shaping the right problem can have an impact even when I don’t own the implementation. The Perfetto extensions idea was worth that full effort. We were already building plugins to modularize the UI, but they weren’t enough: teams had to open source all their plugin code, which wasn’t an option for many internal use cases. So before building anything new, I took the problem and my proposal to my manager, teammates and the client teams. I ended up writing two RFCs, having several 1:1s and giving a couple of talks, refining it as the feedback came in. In the end, I designed and implemented macros as “lightweight extensions”: a way to automate actions in the UI without writing a plugin. Extension servers took the idea further by letting teams share their macros. Instead of implementing every requested feature ourselves, we gave teams ways to adapt Perfetto to their own needs. Dozens of teams inside Google now use macros and extension servers, and several other companies use extension servers internally too. The more often I go through this process, the easier it becomes. When I show genuine interest in someone’s problem, ask useful questions or help solve it, they remember. They start coming to me earlier and bring me into conversations with other people facing related issues. That gives me a wider view of what is happening across the organization, making it easier to spot patterns and build things people actually need. Solving one of those problems brings me into more conversations, and the loop continues. Those successes build the kind of trust that comes from long-term stewardship . Early on, I had to turn many of these ideas into something real myself to prove that my judgment was sound. Over time, my manager and org gave more weight to my assessment of what mattered. That allowed me to influence the roadmap without needing to own every project. This differs from the idea that becoming a staff engineer means replacing technical work with meetings and coordination. For me, conversations are inputs into what I build, not the end result. That is what I wanted my mentee to understand: finding problems worth solving isn’t separate from the rest of the job. It comes from staying engaged with people’s work long enough to see what no single request can show you. These workarounds used bookmarklets to run JavaScript against Perfetto’s internal UI APIs.  ↩︎ The original proposal was to use a transparent cache for repeated queries and faster reopening of large traces. As I worked through it, I realized repeated queries were better served by keeping sessions warm in memory, whereas reopening was better served by explicitly exporting a trace into a format designed to load quickly. A transparent disk cache could also retain multi-gigabyte files without the user realizing and would need a new system to manage their lifetime. The proposal was ultimately replaced by warm sessions and streaming table export .  ↩︎ These workarounds used bookmarklets to run JavaScript against Perfetto’s internal UI APIs.  ↩︎ The original proposal was to use a transparent cache for repeated queries and faster reopening of large traces. As I worked through it, I realized repeated queries were better served by keeping sessions warm in memory, whereas reopening was better served by explicitly exporting a trace into a format designed to load quickly. A transparent disk cache could also retain multi-gigabyte files without the user realizing and would need a new system to manage their lifetime. The proposal was ultimately replaced by warm sessions and streaming table export .  ↩︎

0 views

Was

In 1989, Jonathan boards a plane to Manhattan, Kansas, where he rents a car, explaining to the man at the Hertz desk that he’s dying. In 1875, in the same place, a young girl named Dorothy gets off a train with her dog Toto, expecting her Aunt Emma to meet her, but Emma isn’t there. Years later, Dorothy will have a breakdown while in school, as the substitute teacher—a man named Frank Baum—watches in horror. In 1939, in Culver City, California, a young Judy Garland tells her makeup artist a story which might be confession or might be fabrication or might be both. Was is a story about The Wizard of Oz and it’s a story about fantasy and reality, two ways of seeing that seem forever intertwined. Maybe, in the end, they are the same thing: each a kind of choice that creates a world that is never quite what it was, never at all what it seems. View this post on the web , reply via email , or become a supporter .

0 views

Solod 0.3: Concurrency, JSON, more safety

Solod ( So ) is a subset of Go that translates to regular C — with zero runtime, manual memory management, and source-level interop. It's designed for two main audiences: At the end of the v0.2 post , I said the obvious goal for the next release was concurrency, along with the stdlib packages that support it. That's what v0.3 is about. So now has threads, channels, worker pools, mutexes, and atomics — enough tools for parallel data processing or handling network connections. This release also adds a streaming JSON package, a bunch of safety checks (escape analysis, leak checking, nil-pointer panics, stack traces), and proper and commands. Threads • Channels • Worker pools • Sharing state • JSON • Safety net • Tooling • Wrapping up The new package is the foundation. It provides real OS threads, backed by pthreads. If you're familiar with Go's goroutines, the code will look similar — but there are some important differences. Solod doesn't support closures, so takes a function and an argument, instead of just a like you'd expect in Go. Other important differences: starting an OS thread isn't free, and you always have to on it (or it), or it will leak. That makes a good fit for a small, fixed number of long-lived threads — but not for thousands of short-lived tasks. For those cases, it's better to use a pool (shown below). Threads in Solod communicate with each other through channels, like goroutines in Go. A channel carries values of a specific type. By default, sending or receiving on a channel blocks until both sides are ready, so a channel also works as a synchronization point. A couple of So-specific moments here. When you create a channel, you give it an allocator ( in this case), and you call when you're done with it. Also, writes to a pointer you pass in, instead of returning the value directly. It returns a , which is when the channel is closed and empty. So, a typical loop in Go becomes in Solod. Allocators are a key concept in Solod. The language doesn't allow hidden heap allocations, so any function that needs to allocate memory must take an allocator (the interface) as its first argument. Buffered channels can hold a limited number of values without having a receiver ready — just pass a non-zero size with . If you don't want to block forever, use or with a duration. They return or instead of getting stuck. Threads are expensive, so spawning one per task doesn't scale. For handling many short-lived tasks, use : it uses a fixed number of worker threads that take tasks from a queue. works similar to Go's — it blocks until all submitted jobs are finished. This program takes about 200 ms to run (even though there's 800 ms of total work), because 4 workers run concurrently. You might think OS threads are much slower than Go's goroutines, but for pools, that's not the case. On realistic workloads, is usually only about 10% slower than Go, whether the tasks are CPU-bound or waiting on I/O. Channels are a different story: handing off work between threads requires a kernel wakeup, while Go does this in user space, so it can be several times slower. Check out Go-flavored concurrency in C for more details. One way to share state in Solod is by using channels to communicate it. However, sometimes you just need a shared counter or a lock. For that, the new release introduces the and packages. Here's an example of an atomic counter being updated by 50 tasks running on 4 threads: A regular incremented with would cause a data race and give a different result each time. Here, the result is exactly 50,000 on every run, thanks to the atomic counter. The package provides , , , and types, all of which are lock-free and safe for concurrent use. For anything more complex than a counter, use , which provides , (a condition variable), and (runs a function exactly once). One thing to watch out for: unlike Go, a So mutex's zero value isn't ready to use — you need to it before locking and it when done. Go's relies on reflection to marshal arbitrary structs. Solod has no reflection, and uses a different approach: a token-level API. You read and write one JSON token at a time, and the and types take care of the syntax — adding commas and colons, checking UTF-8, and rejecting bad input. Encoding is done through a series of calls that match the structure of your document: Decoding pulls one validated token at a time with . You can check each token with and read its value using typed getters like , , or : This is a simplified example that works only because the decoder doesn't allocate any memory. In a real-world situation, you'd need to use an allocator . The decoder works the same way whether you're using an in-memory document ( ) or reading from a stream with an ( ). This means you can decode data directly from a source without having to buffer the entire message first. Both the encoder and decoder use minimal memory and will reject invalid JSON or non-UTF-8 strings. As you can see, API is low-level and not nearly as ergonomic as it is in Go, especially when it comes to decoding. But on the bright side, it's 10 times faster and almost doesn't allocate, unlike in Go. Solod compiles to plain C, which is fast but not very forgiving: if you use an out-of-bounds index, dereference nil, or divide by zero, you get undefined behavior that could crash the program or silently give wrong results. The new release addresses some of these issues. Escape analysis . Returning a pointer to a stack-allocated value is a classic C footgun. So now catches the common cases at compile time: While the escape analyzer doesn't catch every case, it's still quite useful in practice. I actually found a couple of dangling pointers in the standard library code with it, even though I was sure there weren't any. Leak detection . Solod has no garbage collector, so a forgotten is a real memory leak. helps catch these leaks: it wraps an allocator and keeps track of every allocation and free that goes through it. This way, you can monitor the program's memory usage in real time instead of guessing. Wrap once, allocate memory through the tracker, and have a background thread log the stats at regular intervals: The tracker is lock-free and only uses a few atomic operations for each allocation, so it's cheap enough to keep enabled in production. Nil-pointer panics . If you try to dereference a nil pointer, it will cause a panic at runtime instead of a raw segmentation fault: Stack traces . When a program panics, the flag controls what happens next: Stack trace frames represent each function in the call chain: The same system handles assertions like slice bounds, index-out-of-range, , and similar checks. Instead of calling C's , they panic in a way that respects the flag. There's also a new flag that enables C sanitizers ( and by default) to help you catch more issues during development: 'so test' and 'so bench' . Solod now has built-in test and benchmark runners. finds functions in a package's subdirectory, creates a runner, transpiles it, and runs it. does the same for . A typical package layout with tests and benchmarks looks like this: There's also a quick check for memory leaks: gives you a tracking allocator (described in the 'Safety net' section above), and the test will fail if anything allocated with it isn't freed by the end of the test. Fuzzing . Since Solod is a strict subset of Go, any So package is also a valid Go package. This means you get Go's built-in fuzzer for free, making fuzz testing pretty easy. So's package takes advantage of this by using Go's own as an oracle, making sure that every JSON document accepted by So is also accepted by Go. Automatic linking . The new directive lets a package specify which C library it needs, and gathers these libraries and passes them to the C compiler. The standard packages already use the new directive, so importing links with , and or links with — you no longer have to set manually. With v0.3, Solod reaches an important milestone: a program can now do multiple things at once. The JSON package gives programs a standard way to communicate, and the safety checks help prevent silent failures — both during development and in production. There's still a lot to do, of course. In the next release, the standard library will keep growing, and the language and tooling will get better to make programming in So more convenient and safe. If you're interested, take a look at So's readme — it has everything you need to get started. Or try So online without installing anything. Go developers who want low-level control without having to learn another language. C developers who like Go's style.

0 views
<antirez> Yesterday

Being Linux Torvalds

(This blog post was adapted from the transcription obtained from my YouTube video at https://www.youtube.com/watch?v=l6lxgYeVZqs) When Linus Torvalds developed the first Linux kernel, he had studied the Minix sources, he had studied computer architecture, he had the base knowledge needed, and he was obviously a very brilliant programmer. But that operation of writing a minimal yet working Unix kernel for the 386 (at the beginning Linux was, let's say, mono-architecture) was something within the reach of many other programmers and students. Many in the sense of, I don't know, 0.1%, one in a thousand, one in ten thousand. Obviously most people are not able to do this kind of feat, but a lot of people are. If you look at Hacker News in the latest years, you'll see how many projects of kernels written in C, microkernels implemented from scratch, kernels written in Rust, kernels made in all sauces and manners, small Unix systems created vertically for the Raspberry Pi, operating systems for the ESP32 and so forth. Writing a kernel is not something within everybody's reach, but it is something that many can complete, if they put enough effort into it. Then, of course, not everybody will do it well. He is a genius programmer, without any doubt, so he did it better. And yet, of Linus there is only one. This implementative capacity of his, in fact, would not tell us much about him: what we should focus on, instead, is what happened later. ## He stopped writing code Among the maintainers of the famous open source projects, he was one of the very few that, very early in the history of the development of Linux, almost completely stopped writing code in order to concentrate on the leading of the project. On being the leader, the coordinator, the single mind holding the clarity about what the goals of the project must be, and so on. And this is a rare thing. Many maintainers (myself included, for a long time) continue instead to implement things directly, to not delegate much, and so forth. This also starts from a different idea of software. Linux, necessarily, had to grow immeasurably: it is in the quality itself of a kernel that wants to embrace many devices, platforms, subsystems, and to continuously adapt to the times, to the needs of the new software, to the hardware that comes out little by little. So this was not a mistake. Redis, on the contrary, could remain something self contained. The other day I received a pull request on linenoise from Dr. Richard Hipp of SQLite: he too aimed at stability, at minimalism, at performances, but always keeping the code base very small, and he continued to write code for a very long time. Linus, instead, no. He understood immediately that he had to donate his time to something that was more important, for a project destined to become very big compared to what is the implementative capacity of a single person. So he became the project leader, the one that owns the ideas, the direction. And what is it that Linus does, then? He does not look at every patch line by line, every time. Of course it also happens to him to look deeply into a single implementation, in order to understand what is going on. It happened to him, over the years, to write some new subsystem, or even to rewrite one: I think he did it once with the USB layer, many years ago, and he did it with the virtual file system, that at some point I believe he reimplemented, changing the structure of the inodes and of the inode cache, and he did it for several other reasons. From time to time he continued to program, when he created Git, and so forth. But for the most part he does not look at the patches singularly, in detail, line after line: he communicates with the maintainers of the subsections, and understands if a given feature or a given direction is, or is not, a road to take. So, to say it in Brooks' terms, in Mythical Man Month terms, Linus holds the design concepts of the kernel, and continues to dialogue with everybody below him in the hierarchy of the kernel so that the kernel goes towards a certain direction. So that the developments go towards a certain direction, both from the implementative point of view (how these developments are implemented, what is the quality, what is the implementative idea in the very way the code is written), and from the design point of view: what is it that we want to do, what we don't want, what is the best strategy for the modules, for the scheduler, for the hardware support, for the integration of Rust or not. All this stuff here. Now, I believe that this was the real genius of Linus. He is not just a very brilliant programmer: there are others. He is also a maintainer, an incredible designer, and one capable of handling a huge project ideas and structure in a coherent way, dialoguing with many other people. This thing is not for everybody. ## We are Linus, now Now, when we program with the artificial intelligences, we are exactly that same thing. We are Linus Torvalds, not always with the talent that he has, but the role we should assume, in the projects where we don't do the review of every line of the code, is exactly of that type. It is exactly the role that he has. Only, the thing is simpler to dominate: unless we use a lot of agents in parallel, it is substantially simpler to dominate than a multitude of patches arriving from different ways. But it is much faster. It is as if, instead of interacting with a team composed of many people at human speed, we interacted with a team composed of one, two, three people, based on how many parallel branches of our project we are developing in that moment, but that are much faster, so they give us immediately a much faster feedback. This slightly changes the modality of the work, but in my opinion for the better: it is easier, less context switching, fewer people to deal with, many fewer problems due to the character, the attitude, and so forth. So, if we think that this role is important, we must not think that automatic programming is "I put the prompt, and the thing writes". Vibe coding is a wrong idea of what automatic programming is, and of what automatic programming will be for the majority of people. Vibe coding is a very interesting thing for who does not have technological abilities and wants anyway to have an impact on the construction of their own tools, and so forth: so, welcome, because it democratizes the possibilities. But it is not that. 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. And since not everybody is able to do it so well, automatic programming as well has need of talents that talk with the agents, that check the ideas, that know which are the implementations to do and the ones not to do, the way of communicating with the agents in order to make them do the best work, putting there those design hints that a great programmer intuits, that a good programmer intuits and manages to precompute. So automatic programming, when it is done well, means to assume the role of Linus. And this thing can be done well, it can be done badly, it can be understood, or it can instead be debased. And it is also something that needs training, that needs to be learned, exactly as Linus had to learn it: he surely had an innate talent for this, but he passed from "I implement everything" to that capability of handling a symphony, of being the orchestra director. That, for me, is the lesson of Linus, and it is one that should immediately be used as an argument of contrast for those that say that, well, with the LLMs programming is easy for everybody. Comments

0 views
tekin.co.uk Yesterday

Overriding Rails’ default validation error message format

This is a followup to my recent lightning talk and writeup about i18n in Rails and how it can be useful even when we’re not translating our applications. In the talk and writeup I describe how we can change the Active Record error message format from the default of and drop the attribute prefix, giving us more flexibility in how we phrase our error messages. The downside to this is that it results in the default Rails error messages being output as incomplete sentences. It’s since occurred to me that it’s possible to avoid this by replicating the default validation error messages in our app’s locale file with the attribute prefix as part of the message , rather than the format! So we end up with a locale file that looks something like this: With that in place, we’re free to define bespoke validation errors for specific attributes and models without the attribute prefix, whilst also preserving the default error messages as a fallback.

0 views
Ahmad Alfy Yesterday

Testing Google’s “modern-web-guidance” skill against a real React app

LLM-assisted frontend work has a particular failure mode. The model confidently writes code that was best-practice in 2021. It reaches for , hand-rolls a dark-mode toggle with a class on , or disables the submit button to “prevent” invalid input. None of it is wrong exactly. It’s just a few years stale, because the training data is a few years stale and the web platform moves faster than that. Google Chrome’s skill is a direct attempt to fix that. It’s not a linter and it’s not a codegen tool. It’s a search index over a curated set of best-practice guides , meant to be consulted before you write HTML/CSS/client-side JS, so the pattern you reach for is the current one. I wanted to know whether it actually earns its place in the loop. So I pointed it at a real codebase, the React frontend of a project-assessment internal tool I’ve been building, and treated it as an auditor. This is what came back. There’s no magic. It’s two commands over : returns a ranked JSON list. Each hit has an , a , the web , a , and a semantic score. returns the guide as markdown. That’s the whole interface. The intelligence is in (a) the quality of the guides themselves and (b) whether the semantic search puts the right guide in front of you. Everything below is a test of both. The app is a Vite + React 18 questionnaire. You answer about 10 questions, it computes a recommended tech stack client-side, and you can save, label, and annotate assessments. It runs to 42 source files. What matters for this exercise is that it’s form-and-input heavy but has no images and no marketing-page concerns. So the relevant guidance is going to be about forms, inputs, theming, and layout, not LCP hero images. I did a quick inventory first. The tells were immediate: Then I let the skill tell me what to do about each. I searched for . The top hit came back at 0.75 similarity , the highest of the whole session: The app’s current theming is a wall of light-mode hex: The retrieved guide is refreshingly opinionated about what’s mandatory versus optional. The two non-negotiables: That single declaration is the highest-leverage line the audit surfaced. Without it, even a perfectly hand-themed dark palette leaves the native scrollbars, widgets, and the initial paint canvas stuck in light mode. That’s the exact “white flash on load” that makes a dark site feel broken. Beyond the mandatory two lines, the guide shows how to define color tokens with , so each token carries its light and dark value in one place. Applied to this app’s theme file, the change is small. Every hardcoded hex becomes a pair, plus the two mandatory declarations: And updating the is just one line: (The dark values are illustrative inversions. The point is the shape of the change, not the exact palette.) What surprised me is that the guide doesn’t stop at CSS. It carries a section on the design of a theme toggle. This is part of the guide’s own text. You can read it with , or straight on GitHub in the dark-mode guide . Its UX considerations subsection makes the sharpest call, arguing that you should not build the toggle most of us reflexively build: DON’T expose all three states (system, light, dark). … Two of the three options always produce the same visual result, violating the principle of feedback. Instead it argues for a two-state control, “follow the system” and “the opposite of the system.” It also spells out the edge case that trips people up. If a user pins dark and then switches their OS to dark too, the site must stay dark rather than flip. That’s product judgment sitting inside a CSS guide, and it’s exactly the kind of thing a model won’t reliably volunteer on its own. Finally, because is newer than , the guide hands over the fallback so you don’t have to reason it out. You degrade through , then upgrade with where exists. It also ships a copy-paste script to prevent the theme flash for users who have pinned a non-default choice. That script is a plain inline one, deliberately not and not a module, so it reads the saved preference before first paint. And that brings up the skill’s best structural feature. When a guide leans on anything newer than the long-settled web, it keys its browser-support advice to Baseline . For , the dark-mode guide returned that it’s widely available and has been Baseline since 2022-02-03. For , it returned that it’s newly available and Baseline since 2024-05-13. This matters because it turns “should I use this?” from a vibe into a decision rule. The skill’s own instructions say Baseline-Widely-available features are safe to use unfenced, while newer features must carry the fallback the guide provides, unless you’ve declared a custom browser-support policy. In other words it defaults to safe, and it tells you exactly where the risk line is instead of leaving you to guess. is safe to just ship, while gets a -guarded fallback. That’s the correct call, and it made it without me having to ask. I searched for . That surfaced the guide at 0.50 similarity, with and an guide right behind it. Here’s the app’s save surface, lightly trimmed: The guide’s very first rule is blunt about it. “DO use the element to wrap interactive controls… DON’T use for primary submission buttons.” The rename field and the notes editor elsewhere in the app repeat the same -plus- shape. The practical cost of the current approach isn’t abstract. Because there’s no , pressing Enter in the label field does nothing , and that’s a reflex every keyboard user has. The fix is small, and the guide hands it over directly, including the AJAX-friendly submit handler: Wrap the input and button in a , make the button , and Enter-to-submit comes back for free, along with native form semantics for assistive tech. I’ll give the skill credit for a fair grade, too. One thing the app already does right showed up in the same guide. The save button disables itself while a save is in flight, and the guide explicitly blesses that. “DO disable the button after a valid submission is clicked to prevent double-posts.” This is the opposite of the anti-pattern from the intro. Disabling after a valid click to stop double-submits is good, while disabling up front to block an incomplete form is the dead end. A good auditor tells you what to keep, not only what to change. I searched for . It surfaced a cluster of tightly-scoped guides, , , and , all built around and . This is where the guides go deeper than a model’s default answer. Ask a chatbot “how do I validate a form field” and you’ll usually get an handler that yells the moment you type one character. The guide instead ships a timing matrix : The guide even boils it down to a single rule. “Validate on to avoid premature warnings while typing, and reset error states on as soon as the user attempts a correction.” The modern platform gives you this essentially for free via the pseudo-class, which only matches after the user has interacted. The app’s ad-hoc error paragraphs are accessible, which is another thing it got right, but they’re wired by hand where the platform now has a purpose-built primitive. The guide’s section 3 covers , , and , the attributes that tune autofill and the on-screen keyboard. None of the app’s inputs use them, and this is the one finding where the honest answer is a polite no. The questionnaire is almost entirely , where you pick one of a handful of options. Radios don’t take an or an token, because there’s no keyboard to optimise and nothing to autofill. The only free-text fields in the whole app are a “label” and a “notes” box, and neither maps to a standard autofill value. So the guidance is correct in general and largely irrelevant here, and noticing that is the actual work. The tool returns a rule. Deciding it doesn’t apply to a radio-driven form is a judgment call it can’t make for you. This is the clearest example in the whole audit of why the skill is only half the loop. One line from the section does still land universally, though. Text inputs should be or larger, because anything smaller triggers an auto-zoom on iOS Safari the moment the field is focused. The app has in two files. The well-known modern fix is (dynamic viewport height), which accounts for mobile browser chrome that ignores. On iOS Safari, is measured against the expanded viewport, so the bottom of a layout sits behind the address bar. My query returned the broad and guides rather than a laser-focused “use dvh” atom. The right answer is almost certainly inside those guides, but the search didn’t hand me a -titled hit the way it did for . Which is a fair segue into the honest assessment. is not going to catch your bugs and it won’t rewrite your components. What it does is remove the single most common source of stale frontend code, the confident-but-outdated pattern. In one afternoon pointed at a real app, it correctly flagged a missing declaration, a set of forms that skip native submission, a validation approach that predates , and a pile of missing input attributes. For each one it handed over current, Baseline-checked, copy-pasteable guidance, while also telling me which of my existing choices to leave alone. One reframe stuck with me. It’s less a tool you run and more a standard you consult . The best time to reach for it isn’t during a cleanup audit like this one. It’s the moment before you write a component, when the model in the loop (human or AI) is about to reach for the pattern it already knows. Half the time, the pattern it knows is three years old. This is the cheap check that catches it. Zero elements. Every data-entry surface is a bare plus a . No , , or on any input. A hardcoded light theme. defines tokens as literal hex values, with no , no , no dark variant. in two places. Some genuinely good instincts too, like / grouping, on errors, and . The guides are high quality. They read less like scraped blog posts and more like a curated reference assembled by people who live in the web platform, close to the specs and the browser internals, but writing for the developer who actually has to ship. The mandatory/optional split, the timing matrices, and the “don’t build a three-way toggle” UX arguments all read as earned judgment, not a spec dump. Baseline-keyed fallbacks where a feature needs one. This is the single best thing about it. It converts “is this safe?” into a date comparison and provides the exact fallback when the answer is “not yet.” You don’t have to look for the fallback, it comes with the guidance. It grades fairly. In two places it validated code the app already had right. An auditor you can trust to say “keep this” is one you’ll actually keep running. Framework-agnostic by design. Every guide is HTML/CSS/DOM, and adapting the pattern to React was trivial. Nothing assumed a framework, so nothing fought mine. It’s local, self-contained, and keyless. The semantic search runs on your own machine through a small on-device model, so the matching itself makes no network calls and there are no API keys to manage. The npm package ships with no extra dependencies, which keeps latency low and the supply-chain surface small, and the CLI can run fully offline. By default the tool reports anonymous usage statistics to Google, including your search queries and guide retrievals, which you can turn off by setting . It doesn’t read your code. You (or your agent) do. This is the big one, and it’s worth being exact about, because it changes how you run the skill. Nothing in this audit was automatic. The app had to be read, the suspect patterns spotted, each one turned into a search phrase, and the returned guidance compared back against the actual lines. There are two ways to do that. You can drive it by hand, deciding what to search, reading the guides, and applying them yourself. Or you can hand the whole loop to a coding agent, which is what I did here. The agent inventoried the frontend, chose the queries, retrieved the guides, and did the comparison, while the skill only ever answered “here is the current best practice for X .” Either way, the skill supplies the standard and something else supplies the code-reading. Point it at a codebase with no idea what you’re looking for and it hands you nothing back. Semantic search has a recall ceiling. hit at 0.75, but the answer never surfaced as its own result. When a query returns only broad category guides, you have to retrieve a large omnibus guide and read it yourself, which brings up cost. The guides aren’t small, but the skill is upfront about it. Every search result carries a in its JSON. The guide reports about 4,500, and about 7,100. I didn’t measure those myself, because the tool hands them to you before you fetch, so you can weigh the cost. Retrieving a few of them still meaningfully fills a context window. That’s fine for a deliberate audit, but something to watch if you wire it into every edit.

0 views
Jim Nielsen 2 days ago

Podcast Notes: Ed Catmull on David Senra

Ed Catmull, co-founder of Pixar and former president of Disney Animation, was on the David Senra podcast and I quite enjoyed the interview. (If you like the interview, you should read his book .) Ed talks about what he considered his job to be: get the dynamics right for groups of people working together. To do this, he would pull people out of meetings, make groups smaller, make them bigger, just constantly work on fine-tuning getting the right people together at the right time with the right feedback (without ego). Granted he didn’t always do it, but that was his goal. He says: This is so important to get [these group dynamics right] because this is what our product is based on: getting this group of people to work well together. So paying attention to the dynamics of the room is the job. They’re the ones making the movies. I’m not making the movie. I’m just trying to get them to work well together. When Steve Jobs came to Ed and essentially said, “We’re gonna bet everything at Pixar on Toy Story , and the same week that Toy Story is released in theaters we’re gonna IPO.” What did Ed think? I thought it was crazy [laugh] I’ve learned a lot in this process. He was right. I love this frank exchange and Ed’s ability to go from “I thought he was crazy” to “I learned”. In a creative endeavor, where so many earlier iterations suck, how do you gauge whether you should keep going? Because to keep going can make you seem a tad crazy, e.g. “This sucks — let’s keep going!” So how do you know whether you should keep investing in it? Ed’s answer: What’s your basis for proceeding? For me, the basis was: what’s the spirit of the team? Because we all know it [sucks] but if they’re all working together — they’re laughing, and [doing things] together — then you say, “Let’s keep going. Let’s keep trying to solve it.” I love this. He didn’t say, “You open a spreadsheet and do a cost analysis.” He said he looks at the spirit of the people working on it. It really backs up what he said his job is: find and help groups of people work together. His attitude seems to be: a group of people with the right dynamics can solve anything. Ed talks about Pixar’s willingness to take on hard problems because they believed a hard problem led to better outcomes (because few others were willing to do the hard work): A hard problem is more likely to lead to an interesting film. If it’s easy, then it’s more derivative. You know how to write a script, you know what the three-act structure is, you know all these elements of storytelling, and you put together all the pieces and you got a story. Is it a great story? Is it emotional? Sometimes yes, sometimes no […] It’s fairly easy to come up with something that is mediocre — and it’s cheaper too. If you take on hard things then you need to spend more time trying to figure out how to be different in what you’re doing. So if you take on a hard problem and you just keep pushing at it, then the fact that it was hard is what is going to make it different. Stated again for emphasis: “ the fact that it was hard is what is going to make it different ”. And it’s the execution that’s hard, not having the idea: If you’re going to make a movie about a rat that likes to cook, that is not a slam dunk. A lot of people want to keep their projects secret, but this is one where you could tell everybody: “We’re going to make a movie about a rat that cooks!” His point being: nobody can steal that idea and make it good. Execution is everything there. And if you look at it and say, “Well that’s too hard. How could you make a film about a rat that likes to cook?” You’re skirting the hard work, which is exactly what is going to make you different. Don’t skirt the hard thing. If you do something that’s hard, that’s the differentiation. Lastly: I love Ed’s perspective on mission statements: The reason [we never had a mission statement] is because a mission statement is an answer, when typically we should always be asking questions, like “What are we doing?” His point being: if somebody asks “What are we doing?” and you immediately go back to the mission statement and say, “Oh, well we’re doing this” that’s not good. It’s better to always be questioning. “Are we doing the right thing? Are we going in the right direction?” You should always be wrestling with those questions. Reply via: Email · Mastodon · Bluesky

0 views
Stratechery 2 days ago

2026.30: The Copium Wars

Welcome back to This Week in Stratechery! As a reminder, each week, every Friday, we’re sending out this overview of content in the Stratechery bundle; highlighted links are free for everyone . Additionally, you have complete control over what we send to you. If you don’t want to receive This Week in Stratechery emails (there is no podcast), please uncheck the box in your delivery settings . On that note, here were a few of our favorites this week. This week’s Sharp Tech video is on Meta’s maddening messaging . Chinese Models and Frontier Futures . Kimi K3 is a very good model — so good that everyone from Wall Street to the U.S. government is suddenly worried about the U.S.’s position in AI. In fact, the threat isn’t new — and the frontier labs advantage is still real. In this week’s Stratechery Article and Sharp Tech episode I break down what has and hasn’t changed in AI, and explain why the biggest danger is U.S. policy around cybersecurity. Solving that problem will require understanding and accepting the reality and nature of Chinese competition, even if that leaves OpenAI and Anthropic to fend for themselves. — Ben Thompson What Happened to Hugging Face? As Sharp Tech’s resident normie I’m often baffled by the lingua franca of frontier technology, and this week’s controversy around OpenAI and its cybersecurity snafu introduced several terms that have amused and confounded me for years: everything centered the “Hugging Face” platform and the concept of “sandboxing,” and we were revisiting “the paper clip problem.” Thankfully, Wednesday’s Update synthesized the story in a way that was a bit more legible for the rest of us. Come to understand what happened, and stay to learn where OpenAI appears to have erred and why this mess is arguably reassuring with regard to alignment fears around LLMs.  — Andrew Sharp The NBA And Its Second Apron Bet.  If you’ve been online for the past month and even half-paying attention to the NBA, you’ve probably encountered complaints about the league’s imposition of a “second apron” salary threshold that’s effectively functioning as a hard salary cap. Contenders are being forced to part ways with homegrown stars, others teams have limited room to improve, and fans almost unanimously hate these changes. With the offseason winding down, this week’s Sharp Text explains precisely what the NBA is trying to accomplish , why I hate it, and the stakes for the league as it bets on parity in the shadow of shrinking local TV money, slowing growth, and heavy reliance on national TV revenue.  — AS Who’s Afraid of Chinese Models? — Everyone is worried about Chinese models, but the frontier labs will be fine; we need to enable open U.S. alternatives. Netflix Earnings, Is Netflix Washed?, Additional Notes — Netflix’s earnings were fine, and befitting a mature company whose most exciting days are likely behind them. OpenAI Hacks Hugging Face, What Happened, Alignment and Paper Clips — OpenAI accidentally hacked Hugging Face, but the takeaways are more encouraging than people realize. Two More Cents on the NBA’s Second Apron Era — The problem with the second apron is that it’s working. Plus: July reading recs! DST and Kimi Android, AI, and the EU France Sold Its Nuclear Steam Turbine Champion. Then Bought It Back. Kimi Madness; Xi’s AI Vision and US Questions; Trump’s Election Data Claims; The Connected Vehicle Security Act Summer Top Fives: Coaches or GMs to Get a Beer With and State Flags An OpenAI Model Escapes Sandboxing, Intelligence Will Be a Commodity Market, The Chinese Model Conundrum

0 views

Premium: The Hater’s Guide To Oracle (Part 2)

Good morning premium subscribers! As ever, please ping me at [email protected] if you have any questions. Oracle has one of the strongest mythologies in the tech industry. Ask a regular person and they’ll tell you that it’s “incredibly profitable” and “growing fast,” that it’s “unstoppable,” and that Larry Ellison has the mandate of heaven with regard to the continual sales of software and hardware related to databases and AI. And those people are completely and utterly wrong. The original title of this article was “Is Oracle Dying?” because I assume, when I took a deeper look, that there’d be some sort of debate , some sort of bull case for a decades-old quasi-hyperscaler run by one of the more nakedly-evil CEOs in the history of tech. I assumed — incorrectly, I might add — that Oracle as a business was doing fine other than the ridiculous commitments it made to support the whims of Sam Altman and OpenAI via deals that I believed (and still believe) will kill Oracle . Except it turns out that Oracle has already been on a death spiral for the best part of a decade (if not longer) and has only survived this long by screwing its customers, taking on masses of debt, and — most importantly — more than $85 billion in acquisitions over the last 23 years. Pretty much every major product line outside of databases is a hodge-podge of other people’s innovation stapled together with a legendary contempt for the customer . These acquisitions (and continual price increases ) are the only thing keeping the reaper from Oracle’s door other than margin-destroying GPUs . And that’s why Oracle’s revenue looks like this : After April 2009’s $5.7 billion acquisition of Sun Microsystems , Oracle’s revenues barely kept pace with inflation until December 2021’s $28.3 billion acquisition of Cerner allowed it to create Oracle Health , adding about $6 billion in annual revenue that had 40% lower margins (about 21.7%) than Oracle’s other businesses , though Oracle immediately started closing offices and brutal layoffs to try and bring them up. And as I mentioned above, Oracle’s other plan was to sink a little over $99 billion in capital expenditures since the middle of calendar year 2020 into AI GPUs.  Anyway, let’s see what that’s done to margins - OH MY GOD ! Oracle is a decades-long mission to keep reapplying lipstick to a pig. Billions of dollars of acquisitions have, for the most part, only succeeded in keeping the company’s revenue growth from going negative, and as noted by forensic accountant Howard M. Schilit , this is one of the most well-documented cases of accounting shenanigans being used to cover up that a business is in decline. Today’s newsletter is a sequel to the Hater’s Guide To Oracle , where I told the sordid tale of how Larry Ellison grew a massive, lucrative business out of a database business that one reporter once told me was a “ law firm with a database company attached ,” an Enterprise Resource Planning (ERP) product that competes with SAP to create the most-annoying way to run a large company, and a business built around licensing Java that exists mostly to email people and say “you need to pay us for Java or we’ll sue you.”  Then, as I’ve mentioned, there’s Oracle’s cloud infrastructure business, a decade-old also-ran that was meant to compete with Microsoft Azure and Amazon Web Services, but only managed to catch up following the advent of AI GPUs and a movement where all it took to party was buying billions of GPUs and saying “gosh darn, we love AI.” I originally started drafting this as a much tamer piece where I’d ask whether Oracle was dying, but as my editor and I started digging into the research, it became obvious that not only is Oracle dying , it’s been dying for years , kept alive through decades of acquisitions and a desperate and dangerous commitment to generative AI. And AI, I believe, will be what eventually kills Oracle dead.  In the past, all Oracle had to do to survive was buy somebody else’s company and replace its flagging revenues with theirs, turning the screws on their customers and laying off as many people as necessary to balance the books. While chaotic and decaying, Oracle’s empire has kept above water by never overextending itself, always keeping a positive free cashflow , and generally avoiding buying into industry hype cycles outside of whatever SaaS vehicle might potentially plug the gap in its earnings.  Yet with AI, Oracle broke its long-standing trend of letting someone else figure out the innovation, choosing instead to build its own cloud infrastructure, spending more in capex in its last fiscal year ( $55.6 billion ) than it did in the previous nine years combined ($50.4 billion), tripling its debt from $56.91 billion in FY2017 to $167.4 billion in FY2026, a year that ended with its free cashflow sitting at negative $23.69 billion.   For comparison, Oracle has had positive free cashflow every single year since 2001, including the Great Financial Crisis and COVID. Oracle has doomed itself with its commitment to the AI bubble. It has committed to building 7.1GW of data center capacity for one company — OpenAI — as part of a $300 billion, five-year-long contract that requires it to build an impossible amount of capacity in an impossible period of time for a client that could never afford the $70 billion or more in annual costs to make any of it worth it.  Today I am going to talk trash on what I consider to be one of the single-worst companies in the tech industry that’s survived only through financial engineering and never, ever overextending itself.  With revenue plateauing and customers in revolt, Oracle’s future already looked murky, but with the power of AI — and $95 billion in FY2027 capex — it’s becoming increasingly clear that this may be Larry Ellison’s last dance with Silicon Valley. This is the Hater’s Guide To Oracle Part 2, or AIpoaclypse Now.

0 views
Unsung 2 days ago

Chrome’s breaking and entering

I got pissed at Chrome the other day . This is not the first user-hostile thing Chrome did – off the top of my head, I remember the updater fiasco from some years ago, and the more recent auto-installation of a 4GB file – but as you’ll see, this one is squarely in my wheelhouse. The transgression: Chrome took over a shortcut on my Mac – Ctrl+G – and it used it to throw me into Chrome’s version of Gemini that I have never used or was interested in using. Moreover, it decided it’s okay for Ctrl+G to put me there even if I pressed the shortcut outside of Chrome . I was never asked by Chrome if it’s okay to do so. The way I found it’s installed it was in a very unpleasant way: I tried to use Ctrl+G in my coding editor to jump to a specific line, and I got this instead: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/1.1600w.avif" type="image/avif"> Stuff like that can make you feel like you lost your mind. I have no idea what this window is supposed to do, where did it come from, or even – initially – why it appeared. Note that it doesn’t even identify itself as either Chrome or Gemini, unless you read the scary caveat. It feels like the UI equivalent of breaking and entering. Unsurprisingly, the pop-up doesn’t confess to stealing the shortcut, or allow you to toggle it off in any way: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/2.1600w.avif" type="image/avif"> It is possible to undo that behavior, but one has to connect it to Chrome first, and then go deep into its settings – first by clicking on “AI innovations,” and then by clicking on “Gemini in Chrome” – to find it: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/3.1600w.avif" type="image/avif"> Let’s not beat around the bush: This is effectively malware behaviour. It’s bullshit. It’s cancer. It’s deeply disrespectful toward the user. It’s prioritizing hollow metrics at the expense of everything else . But I don’t want this blog to chase news of the day or feed the outrage machine, so let me try to turn my anger into something useful. Let’s start here: There are some global keyboard shortcuts that are genuinely good. A video call mute shortcut, screenshotting, “next slide” if you’re presenting in Zoom. Everything related to computer operation – volume, brightness, media transport controls – needs to be available regardless of focus or context. (In my keyboard customization essay, I introduced my own global keyboard shortcuts , for example for scanning the next page.) But, an app installing a global keyboard shortcut without user consent is bad. This can never be anything other than opt-in. At the very least, Chrome should have shown me a clear UI that said “We’re thinking Ctrl+G would be fun for you to use. You okay with that?” and a button for me to press to confirm. (Note: Ctrl+G is the shortcut for Macs. As far as I can tell, on Windows it is Alt+G.) From people’s reactions, it seems this shortcut is auto-enabled for a subset of users: perhaps people who used Gemini before, or people on a plan that happens to include it. No matter how specific or small that group is, or how useful they might find the Ctrl+G pop-up, the issue remains: I have never consented to the app doing this. I also partly blame macOS for ceding its responsibilities here. Mac’s keyboard customization features are a mess, and Mac doesn’t have a modern command repository. It’s not just that apps can register global shortcuts as they want, without the user knowing. It’s also that there is no shared inventory of them; if an app “swallows” a shortcut but does nothing noticeable with it, it can be really hard to figure out why a shortcut seemingly just stops working. (Other apps that I remember having problems with “stealing” global shortcuts, and apps that a few readers posted are: 1Password, Notion, and Perplexity. I’d be curious if you have other examples!) In light of macOS’s deficiencies, as an app, if you offer any shortcut customization – especially if you allow global shortcuts – I think it’s important to have a page that lists all of them shortcuts in one place. This is not what Chrome does, as various shortcut options are hidden on various pages in settings. Even Zoom, which is not generally known for having a great user interface, does better here: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/4.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/chromes-breaking-and-entering/4.1600w.avif" type="image/avif"> And, since we’re back to Chrome, what a fall from grace! When Chrome started in the late 2000s, it felt like a browser that had user’s interest in mind, and protected people from ill-behaving websites. Today, it’s the operating system that needs to protect us from Chrome. (Also, don’t call a tab “AI innovations.” It’s tacky as hell. The market gets to decide what’s innovative and what is not.) #attention #google #keyboard #mac os

0 views
Unsung 2 days ago

“Creativity is fundamentally not an efficiency problem.”

A computer science professor Paul Cantrell, on Mastodon : Creative work keeps taking roughly the same amount of human labor / attention / care, even as new technologies accelerate or remove things that used to take time. This is because creativity is fundamentally not an efficiency problem; process is not just the means of producing output, but rather a labor vessel that holds the near-invisible work that is truly important. One can feel the care that goes into creative work without being aware of that work, or even being aware that work of that type exists at all. This feeling is approximate, loose, vague, but cumulative and eventually all-important; work with no care behind it wears thin and tends to fade as people live with it over time. This really resonated with me. Elsewhere, Ginger Bill, in a recent – meandering, but thought-provoking – essay titled “Good tools are invisible” : I constantly see some people praise it not for what actually makes it good, but by taking the things it’s bad at and turning them into a puzzle to have “fun” solving. I’ve had people tell me how “fun” it was to build a macro to handle some one-off text-refactoring problem. But when I looked at what they were doing and how long it took, my honest reaction was: I could have done that in Sublime in a minute with multiple cursors, or just written a quick script. […] That’s what I mean by “invisible tools”. When you’re proficient with your editor of choice—whatever it is—it disappears into the background. But the moment it cannot handle something easily, it stops being invisible. What baffles me is that so many people treat that friction—the effort of working around a tool’s limitations—as the “fun” part, and then advertise it as evidence that the tool is great. […] The text-editor-macro anecdote I mentioned is really about a gap between feeling productive versus being productive . There’s a sensation of cleverness that comes from solving a fiddly problem, and it’s easy to mistake that feeling for actual output. A tool that makes hard things feel heroic and clever feel like an achievement can register as “powerful” while quietly being slow. The honest test isn’t how engaged or clever you felt, it’s wall-clock time and how many mistakes you made getting there. This I had more of a mixed reaction to. I think it’s necessary to expect from tools to get out of the way, but there’s also nothing wrong with having fun with them. My simple go-to example is this: When writing code, I sometimes use Find & Replace All, and am done within a few keystrokes. But sometimes, I press Find and then replace one at a time, jumping methodically through the file, and seeing each string in situ before changing it. I know the tool could do it all for me. I know I could be more efficient. But this intentional slowing down allows me to refamiliarize myself with the code, visit its forgotten nooks and crannies, and make sure I understand where and how the thing I’m changing is actually used. The editor I use allows me to not be efficient when I choose not to be. In my work, flow operates at different speeds; a good tool understands that and doesn’t force me into a particular one. I think ultimately indeed, the tool does need to disappear, and make you be in charge of whatever speed you want to operate at, and how much friction or difficulty you choose to face (do you bump the lamp or not?). But it’s not as simple as always “reducing wall-clock time and mistakes.” Like Cantrell says above: Creativity is fundamentally not an efficiency problem. #ai #craft #flow #toolmaking

0 views