Posts in Go (20 found)

9 months in: building an advanced StarCraft reporting tool with Go & Claude

The story of how I built screpdb, an advanced StarCraft: Brood War replay reporting tool, using Go & Claude over 9 months, and what AI could and couldn’t do along the way.

0 views
Anton Zhiyanov 5 days ago

On interactive Go tours

Over the past two years, I've published interactive tours for five Go releases, from 1.22 to 1.26. I know some of you have read them, and I've received a lot of kind words from you (even some core Go team members reached out) — thank you so much for that! Tour history: Go 1.22 • 1.23 • 1.24 • 1.25 • 1.26 + Go features by version Unfortunately, at some point, writing these tours stopped being fun and started to feel like a part-time job. I'm not really excited about that, so I've decided to stop. I still like Go (well, most of it). I read a lot of Go code, I write some Go code, and I write Solod code, which is also Go 🙂 (Solod is a systems language with Go syntax and a Go-like stdlib). I'm still pretty close to the language and will probably continue to write about it. But the interactive tours story is over.

0 views
Anton Zhiyanov 6 days ago

Go-flavored concurrency in C

Go's concurrency is one of the main reasons people like the language. You write , send values through channels, and the runtime scheduler runs thousands of goroutines on just a few OS threads. It feels effortless. None of that machinery exists in C. Which made me wonder: how close can you get to Go's concurrency model using only POSIX threads? Obviously, native OS threads can't match the efficiency of lightweight goroutines, but what is the actual cost, when does it become a problem, and is there any way to at least partially avoid it? I ran into these questions while adding concurrency to Solod (So), a strict subset of Go that translates to plain C, with no runtime and no garbage collector. In the end, I came to the conclusion that you can do quite a lot with pthreads — as long as you're honest about the tradeoffs. This post is about the POSIX threads-based concurrency model I chose, the benefits it offers, and its limitations. Mutex/Cond • Atomics • Pool • Channel • Performance • Design • Wrapping up Everything in So's concurrency stack is built on two basic POSIX primitives: the mutex and the condition variable. is a thin wrapper around : Since So translates to C, this is basically a struct that holds a and a function that calls . Here's the transpiler output: That is the whole translation — the generated C is a near-mechanical mirror of the So code, only noisier. From here on, I'll mainly show the So version, but I'll also provide the C code for those who are interested. There's nothing exciting here: is a pthread mutex wrapper that panics if something goes wrong (which is rare). The companion primitive is , a wrapper around . It's the standard "wait until a condition holds" tool, associated with a mutex: These two types — and — are the foundation. Other concurrency tools — , the thread pool, channels — are built using a mutex and one or more condition variables. This has several effects on performance, as we'll see later. Not everything needs a lock. So's mirrors Go's: , , , , , and a generic , all with , , , and methods. The nice thing is that these don't need pthreads at all. They map directly to the C compiler's builtins — the same hardware instructions that Go's compiler emits. So there's no reason for them to be any slower, and they're not: Each number is the cost of one operation on a single thread. is a good example of using atomics effectively. Its fast path only needs a single atomic load — after the given function runs, every future call to checks a flag and returns: To actually run code concurrently, you need threads. The type wraps and its related functions: Consider this function: Usage example: It might look like , but that's just on the surface. starts an actual OS thread, not a goroutine. You have to eventually call to join or it, or else its resources will leak. Also, OS threads are expensive to create — they're nothing like Go's goroutines, which only need a few kilobytes of stack and start up in nanoseconds. That's exactly why you usually don't want to call inside a loop. For tasks that are short-lived or happen often, it's better to use a pool of long-lived worker threads and send tasks to them. to the rescue: Usage example: The first argument to , , is a memory allocator. Solod avoids hidden allocations, so anything that needs memory takes an allocator explicitly — here it backs the pool's task queue. Under the hood, a is a fixed group of worker threads that pull tasks from a shared queue (a ring buffer). It uses one mutex and a few condition variables: wakes up a worker when there are tasks to do, applies back-pressure when the queue is full, and lets know when everything is finished. It's a classic producer-consumer setup, about 200 lines of code , and there's nothing fancy about it. The heart of the pool is the worker loop. Each thread blocks until a task appears, runs it outside the lock so workers execute in parallel, then records that it finished: This is what separates a pool from a plain queue. bumps as it enqueues; each worker decrements it after running a task, and the last one out broadcasts . sleeps until the count hits zero: The tradeoff is that the number of worker threads is fixed. In Go, a program can handle thousands of concurrent I/O waits because blocked goroutines use very little memory. A So pool can't do this — if all N workers are parked on a blocking syscall, the pool is stalled until one returns. You have to set the pool size based on the workload, instead of letting the runtime manage it for you. Channels are an important part of Go's concurrency model, and So's gives you something quite similar. Just like in Go, it passes values by copy and comes in buffered and unbuffered flavors: is a thin generic shell over one of two engines, picked at creation time: Buffered ( ) is a mutex-guarded ring buffer with and condition variables — like the queue. Senders block when it's full, receivers block when it's empty. The full implementation also checks for , but I left it out for brevity. is the mirror method: block while empty, pop the next value, signal to wake a sender. It also handles the closed channel, returning once the buffer is closed and drained. The rest is this lock-wait-signal core. Buffer source code Unbuffered ( ) is a rendezvous: each send blocks until a receiver takes the value, copying bytes directly from the sender's stack to the receiver's destination without using an intermediate buffer. is the other half: it waits for a published, unclaimed value, copies bytes straight from the sender's stack into (no intermediate buffer), marks it as claimed, and broadcasts to wake the sender back, creating wakeup #2. One hand-off, two wakeups. Copying directly from the sender's stack is safe because of that second wakeup. is a pointer to , which lives on the sender's stack. While the receiver is reading it, the sender is parked in , so its stack frame stays alive. The sender only returns (and reclaims that memory) after the receiver sets and wakes it up. There's no need to copy into a shared buffer because the source is guaranteed to outlive the read. Rendezvous source code As you can see, the API is pretty similar to Go. Now let's look at the numbers. Here's the main tradeoff: pthread-based concurrency primitives are fast when no one has to block, but they get slow when someone does. And it's always for the same reason. Go schedules goroutines in userspace. When one goroutine blocks on a channel and another wakes it up, the runtime moves them between its own queues — no kernel involved. POSIX threads, on the other hand, don't provide a userland scheduler. When a thread blocks on a condition variable, it parks in the kernel, and waking it up requires a syscall. Every hand-off between threads that actually parks pays the cost of a syscall on both ends. You can clearly see the difference in the mutex benchmarks. With 8 competing threads, it all comes down to whether the waiting threads have to park or not: Each number is the average time for a single / pair. The uncontended benchmark runs on one thread, while the contended benchmarks have multiple threads fighting over the same mutex. Notice that So actually wins the first two benchmarks, and for good reason. So's is a plain call with nothing extra, while Go's adds more overhead — like starvation-mode tracking and a runtime that stays involved because a goroutine can be preempted in the middle of a critical section. When nobody parks, that overhead is the main cost, and the thinner wrapper is closer to the hardware. With an empty critical section (the spin benchmark), a waiting thread grabs the lock while still spinning and almost never parks — So wins by 2.8x. The uncontended benchmark (a single thread, no contention) shows the same thing: less code between the call and the lock, so 9ns versus 14ns. The picture flips the moment threads have to park. Give the critical section about a microsecond of real work (the work benchmark) and waiters exhaust their spin budget and park. Now every hand-off costs a wakeup syscall, and So drops to half of Go's throughput. The work is identical in both cases — the difference comes from the parking cost. Condition variables demonstrate this clearly because they always park: Each number is the cost of one rendezvous round: a single broadcast that wakes every waiter and hands control back, with N waiters plus one broadcaster. Pthread-based condition variable is consistently 7-10 times slower. There's no trick to close this gap — it's just the cost of waking up a real OS thread instead of a goroutine. Channels have the same issue because they're built using mutexes and condition variables: Each number is the cost of moving one value through the channel (send plus its matching receive). The number in parentheses is the buffer capacity. The uncontended case fills and drains a buffer from a single thread, so nothing ever blocks — it's just a lock plus a copy, which gives So a slight advantage. But the moment a producer and consumer actually start handing off work, So has to wake up a thread for every transfer that gets parked. It's worst for the unbuffered channel, where every value is a rendezvous with two wakeups: 23x slower. A larger buffer helps a lot — with room for 100 items, most sends go through without waking anyone, and the gap narrows to about 2x. The consequence is that the larger your tasks are, the better pthread-based concurrency works. If you use a channel for fine-grained, value-at-a-time streaming between threads, performance will suffer. But if you use a channel to pass whole work items to a pool, where each item takes tens of microseconds to process, the wakeup cost becomes negligible. The pool benchmarks on realistic workloads confirms this: Each number is the wall-clock time for 8 workers to process the whole batch. Here, So is within 1.1x of Go. The per-task dispatch cost is still present, but it's spread out over real work, and the performance penalty is pretty small. Benchmarking All benchmarks were run on an Apple M1 CPU running macOS. The C code was compiled with Clang 16 using these CFLAGS and mimalloc as the system allocator: The results shown are the medians from several benchmark runs. Each benchmark ran many iterations, following the same logic as Go's own benchmarking. The Go benchmarks used Go 1.26 and . Source code for both So's and Go's benchmarks: conc • sync Here's a summary of the strengths and weaknesses of the pthread-based approach: If you're looking for "thousands of cheap goroutines", the pthread-based approach will let you down. But if you're fine with "a few worker threads handling lots of tasks", it holds up well. Three decisions influenced the way I implemented concurrency in Solod. Pthreads, not fibers . I know there are coroutine/fiber libraries for C that avoid the kernel wakeup cost — single-threaded ones like neco , and multi-threaded ones like libfiber . A userspace scheduler is exactly what would help to match Go in the benchmarks above. I decided not to use one. I wanted something dead simple — an approach I could explain in a paragraph, using tools every C programmer already knows. The trade-off is that you lose some performance with fine-grained blocking, but in many real-world situations, pthreads work fine if you use a worker pool. For me, keeping things simple is more important than saving a few microseconds during task hand-offs. For now, at least. Standard library, not language . Go bakes goroutines, channels, and select right into the language. I decided to keep everything in the stdlib for two reasons. ➀ It follows So's "no hidden allocations" rule. In Go, quietly allocates a goroutine stack, and allocates a buffer. In So, all allocations are explicit: you pass an allocator to and , and you always know exactly where the memory comes from — whether it's the system allocator, an arena, or something else. ➁ A library is more flexible. Since a pool is a regular value, you can have as many as you need, each sized for its specific purpose. In a multi-stage pipeline where each stage needs a different capacity, you can start one pool per stage, each with its own and , instead of being given a single global scheduler. The language stays simple, and the flexibility is in code you can easily read. Timeouts, not select . Go's waits on several channel operations at once and proceeds with whichever is ready first. Implementing it would require a lot of work — a thread has to register interest on multiple channels, block once, and then wake up when any of them is ready — so I left it out. Instead, offers and , which cover two common uses of with a single channel: What's missing is the ability to block on multiple channels at once and continue with whichever one is ready first, as well as the option to mix sends and receives in the same selection. How close can you get to Go's concurrency using only pthreads? Close enough to be useful, but not enough to really match Go. You can wrap real OS threads with familiar APIs — mutexes, condition variables, pools, channels — and the code will look and act a lot like Go, at least until a thread needs to block. But there's no scheduler underneath, so when a thread blocks, it's an actual thread waiting in the kernel, not a goroutine that's paused for free. That's the main limitation of this approach. What you get in return is brutal simplicity. Every primitive is a thin wrapper with no runtime hiding behind it, so the performance is exactly what the OS gives you: fast atomics, fast uncontended locks, and pooled throughput within ~10% of Go on coarse-grained work. But as soon as you switch to fine-grained, one-value-at-a-time hand-offs, the cost of kernel wakeups becomes the main factor, and you'll notice the slowdown. If you think the pthread approach might work for you, I invite you to try Solod . It includes the and packages, along with many others ported from Go's standard library. ➕ Coarse-grained pooled workloads are within about 10% of Go's performance. ➕ Uncontended locks and spin-friendly critical sections perform quite well. ➕ Atomic operations are as fast as in Go. ➕ The implementation is 100x simpler. ➖ Anything that needs to park and wake an OS thread is much slower than Go's userspace scheduler. ➖ The pool can't handle thousands of blocked waiters like goroutines can. "Do this, but give up after a while" (Go's idiom). "Do this only if it won't block" (Go's non-blocking branch).

0 views
Xe Iaso 1 weeks ago

The console wars have been lost

Previously I opined that Valve was about to win the console generation . I couldn't have possibly predicted that both Microsoft and Sony would just self-sabotage so hard that they're both going to lose. Between Microsoft's decimation of the Xbox division , slaughtering off the IdTech team , and continued increases of Xbox hardware prices ; there's nothing to really be excited about with the Xbox. Sure their most recent presentation showed off a bunch of exclusives, but none of them really made me think "wow, I should go get an Xbox to play that". Hell, few of them made me think "wow I should go play that" beyond the Halo remake coming out next month (and really I just want to see how much of a trainwreck that is going to be). Microsoft is also starting to double-down on their in-house games being Xbox exclusives, which really doesn't give me much reason to want to play them because I simply can't buy them without buying an Xbox. Sony also has discontinued porting their games to PC because they're not hitting the (probably impossible) revenue targets that they need to make up for big-ticket failures like Concord . I do have a PS5 that has mostly been relegated to gathering dust when it's not playing YouTube and Twitch duty in the living room, it's likely going to be replaced in favour of my Steam Machine whenever that comes in next year. However nothing that's come out in terms of Playstation exclusives is really compelling, and what is compelling enough just isn't that compelling to want to buy it on Playstation as opposed to just getting it on Steam where I can run it on my tower or on the home theatre PC. Sony also has been raising prices and recently announced that they're killing physical media next generation . It's starting to make me wonder if I should even bother getting the next generation of Playstation. If I can't give people physical games as gifts anymore, why should I bother buying the new console? My husband and I both can't remember why we even got a PS5 in the first place, maybe it so that we could do couch gaming without hearing the fan noise or so that the video streaming experience from the NAS could support HDR. We have a Switch 2 at home, it's mostly there to play Nintendo exclusives like Mario Kart World and the Xenoblade series. If those exclusives were available on Steam, we wouldn't buy them on the Switch 2. Otherwise, everything is via Steam or other PC storefronts anyways. Man, Valve really does win by doing absolutely nothing while the rest of the industry shoots itself in the head. I fear for what happens when Gabe Newell retires and the MBA cancer fully infects Valve.

1 views
Blog System/5 1 weeks ago

Autoconf’s revenge: ad-hoc shell templates

As powerful as Bazel is, sometimes it’s not featureful enough. When using this build system, it’s common practice to wrap it in a launcher script—and in fact, this is natively supported by Bazelisk , Bazel’s native dispatcher that stands for the binary in the user’s . Bazelisk will first download the version of Bazel requested by the project, and then, if exists, invoke it instead of the downloaded binary. is what’s known as a Bazel wrapper and is the point of today’s article. Well, not quite. The actual point of today’s article is to demonstrate a simple trick I learned from the GNU Autoconf and Automake days to implement full-blown conditionals in an ad-hoc template system. But because such trick is trivial once you see it, I have to present it in the context of a modern real-world scenario. So what I’m going to do is guide you through the creation of your very own Bazel wrapper to customize Bazel’s configuration file in ways that the native Bazel tool doesn’t support. Let’s get started. But wait! Take a moment to subscribe. I’m sure you’ll enjoy future posts, and it’s the only way for me to know that they are worth writing in the first place! Template systems are everywhere. Take any static blog generation system and you’ll find some. Take system management tools like Ansible and you’ll find others. Take a cloud orchestration service like Kubernetes and you’ll find Helm. Heck, even Go’s standard library provides a full blown text template system out of the box. There is clear benefit and appetite for these and, surely enough, it’s tempting to use any pre-existing such system in your own project… but if all you need are a bunch of variable replacements, some of which may be only conditionally applied, you can go a long way by not taking any dependencies. A call to or the substring function of your language of choice is all you need. To put this in context, let’s say you have Bazel’s configuration file, which is not very flexible, and that you need to set some arguments based on dynamic values that depend on the environment. E.g. something like this: If you know a little bit about , however, you may squint at that and say: “That’s silly! Make those flags conditional on a configuration and you’re set!” So you try something like this: And… this does not work. Gotcha! Startup flags cannot be placed behind a configuration so there is no way for you to parameterize the JVM’s max heap value passed in . And having to remember to pass from CI all the time is fragile, because you might forget and not get the desired configuration in place. Solving the above is not difficult if we could parameterize the configuration. We might want to write something like this instead: … and then have and be replaced dynamically depending on some runtime arbitrary logic. We can do that via the Bazel wrapper, and this sort of dynamic configuration is a common thing to do from it. So let’s do this. Let’s start with the template logic: Ugly(?) bash syntax but nothing too complicated: The global hashmap tracks variable names and their replacement values. The function inserts a new key/value pair into . (It’s important that the values given to don’t contain -special characters like , backslashes, or the separator we chose—but we control the generation of those values so we are good.) The function transforms the hashmap into a set of arguments of the form and then calls to process the given input file into the given output file. Then, we can plug everything together into a minimal Bazel wrapper: In this new snippet, the function instantiates the file from the contents of via and then calls the actual Bazel binary provided by Bazelisk in . The complexity here may seem overkill, but it’s necessary : while it’s pointless to invoke Bazel in parallel due to its global lock, users will run Bazel in parallel and you must make sure that the wrapper is reentrant. Otherwise, you’ll definitely run into races. The rest of the script in does the actual work to compute key/value pairs to substitute in your now-templated and then delegates to Bazel via . That’s it. This is a barebones implementation of a text template system using bash—and I had to use bash, not sh, to get the niceties of a hashmap —that serves as a launcher. Go try it. By the way, the and nomenclature are inherited from GNU Autoconf’s AC_SUBST primitive . “Great!” I hear you say in a sarcastic tone. “You have just applied string replacements! But what about conditionals, huh? You CaNnOt Do ThAt So EaSiLy!!11!one!” Ah, but you can , and showing you that trick is the whole point of this short article, remember? The necessary insight is that we can use string replacements to comment out lines in the original file. What if we did this: In here, we are defining different configurations for developer workstations and for CI, like we did earlier, but then we are auto-magically picking the default configuration depending on and . How? Well: will expand to the empty string when running on CI and will expand to , so the corresponding lines will be enabled and disabled. And the opposite replacement values will appear when not running on CI. Ta-da! Conditionals. We can make things nicer with a helper function and meta-programming: Don’t panic about that . Just as with the invocation above where we could have issues with special characters appearing in values, we control the arguments to so the is safe. And note that we can even nest conditionals arbitrarily. There is nothing preventing you from doing: Which corresponds to the conceptual equivalent of: Let’s do loops? Sorry no, can’t do! Well akshually… we could do loops. Not by using simple tricks like above, but we could definitely sketch something like this: However, this is starting to look a lot like a high-level parser, not scripting where you glue simpler components together. And if you are headed that way, you are better off transitioning to a proper programming language and a well-known template system. What do you think? Do you hate this already? You can, but note that the whole world runs on this stuff. All of that foundational code behind Linux systems ends up using GNU Automake and GNU Autoconf, and those packages are full of stuff like this in their and files. And you can get very far with just the above constructs if you treat the shell like a real language . The Bazel wrapper that I maintain at work these days grew to almost 1000 lines of code before I pruned a lot of features that had become unnecessary, but it’s still pretty large. We are now transitioning it to a Go-based wrapper for better readability and maintainability… but as we do this, I’m reminded that well-groomed shell scripts give you some flexibility that no other language can match in just a few lines. So, keep things simple. You can do a lot with just a few primitives. As powerful as Bazel is, sometimes it’s not featureful enough. When using this build system, it’s common practice to wrap it in a launcher script—and in fact, this is natively supported by Bazelisk , Bazel’s native dispatcher that stands for the binary in the user’s . Bazelisk will first download the version of Bazel requested by the project, and then, if exists, invoke it instead of the downloaded binary. is what’s known as a Bazel wrapper and is the point of today’s article. Well, not quite. The actual point of today’s article is to demonstrate a simple trick I learned from the GNU Autoconf and Automake days to implement full-blown conditionals in an ad-hoc template system. But because such trick is trivial once you see it, I have to present it in the context of a modern real-world scenario. So what I’m going to do is guide you through the creation of your very own Bazel wrapper to customize Bazel’s configuration file in ways that the native Bazel tool doesn’t support. Let’s get started. But wait! Take a moment to subscribe. I’m sure you’ll enjoy future posts, and it’s the only way for me to know that they are worth writing in the first place! The context Template systems are everywhere. Take any static blog generation system and you’ll find some. Take system management tools like Ansible and you’ll find others. Take a cloud orchestration service like Kubernetes and you’ll find Helm. Heck, even Go’s standard library provides a full blown text template system out of the box. There is clear benefit and appetite for these and, surely enough, it’s tempting to use any pre-existing such system in your own project… but if all you need are a bunch of variable replacements, some of which may be only conditionally applied, you can go a long way by not taking any dependencies. A call to or the substring function of your language of choice is all you need. To put this in context, let’s say you have Bazel’s configuration file, which is not very flexible, and that you need to set some arguments based on dynamic values that depend on the environment. E.g. something like this: If you know a little bit about , however, you may squint at that and say: “That’s silly! Make those flags conditional on a configuration and you’re set!” So you try something like this: And… this does not work. Gotcha! Startup flags cannot be placed behind a configuration so there is no way for you to parameterize the JVM’s max heap value passed in . And having to remember to pass from CI all the time is fragile, because you might forget and not get the desired configuration in place. Basic string replacements Solving the above is not difficult if we could parameterize the configuration. We might want to write something like this instead: … and then have and be replaced dynamically depending on some runtime arbitrary logic. We can do that via the Bazel wrapper, and this sort of dynamic configuration is a common thing to do from it. So let’s do this. Let’s start with the template logic: Ugly(?) bash syntax but nothing too complicated: The global hashmap tracks variable names and their replacement values. The function inserts a new key/value pair into . (It’s important that the values given to don’t contain -special characters like , backslashes, or the separator we chose—but we control the generation of those values so we are good.) The function transforms the hashmap into a set of arguments of the form and then calls to process the given input file into the given output file.

5 views

Now Go Build CTO Fellowship: Season 2

Today, we're releasing the second season of the Now Go Build documentary series. Five episodes featuring technology leaders from around the world solving the hardest problems in healthcare and education.

0 views
Brain Baking 2 weeks ago

Postcard Teas: A Few Impressions

For almost ten years now, we’ve sworn by Mariage Frères when it comes to shopping for high quality loose tea leaves. The nearest shop, however, is in Lille, which is almost three hours away. Their webshop is crude and doesn’t allow for a taste session before buying hence we did buy our fair amount of misses. Yet we remained faithful: the few times that we diverged from the brand ended up in a disappointment. And then I saw someone claiming that London-based Postcard Teas is “even better than Mariage Frères”. My initial reaction to that was “impossible”. I secretly made a note in my journal regardless. When our stock started to dwindle, I dug up that note and said to myself: what the heck, let’s do something crazy and order elsewhere. Postcard Teas is a small shop in London that sells specialty teas by importing directly from the growers. Their unique selling point is hinted in the name: these growers only have a few acres in which they aim to grow the best quality possible. The result is only a few kilograms of yield each year, yet the average price remains acceptable. Each bag of tea you order comes wich a lovely postcard and piece of art depicting a work from the country of origin. I have no idea where Mariage Frères’s tea comes from and love the fact that with Postcard Teas, this knowledge is accessible—even evident. Besides the location and yield, the back of the postcard even contains the grower’s name and a tidbit of bio. Watch China Minutes’ visit to the small shop to breathe in the atmosphere. Meanwhile, I’ll go prepare myself a cup of their summer Darjeeling. Still interested? Great! Yet people outside UK should be warned as shipping comes with a hefty taxation at the border I didn’t mentally prepare for… Just take that into account when you’re browsing their webshop—and don’t forget to compare prices with your usual supplier per , not the deceptive . With that being said, here are some impressions of the teas we tried out: Golden Darjeeling A lovely dark red tea that goes down very well without being too strong. I usually buy first flush/spring Darjeeling and kind of wish I did here as well as that’s usually milder, but this summer Darjeeling is excellent, even if you accidentally let it steep for too long. Contrary to its spring variant, it also handles heat very well, so I usually set it with boiling water. A pure Darjeeling is usually my go-to in the morning or even right after lunch. This black tea is less black than the cheap green powdered teas bought in supermarkets. 4 out of 5 Blounts—Great. Gianfranco’s Earl Grey The first thing that came to mind after opening the bag is: I hope the strong scent does not reflect in the taste. And luckily, it doesn’t. Mariage Frères’ Roi Des Earl Grey is more purgent, up to the point that they might have overdone it. Gianfranco’s bergamots in Calabria pair very well with Kerala’s small Darjeeling tea farms. The structure and colour of the tea is very similar to the previous one, the Golden Darjeerling. This is because Postcard Teas blends both flavours in their shop in London giving them the advantage of carefully choosing both ingredients. Since I love a good Darjeerling, it’s impossible to resist. I do still prefer Mariage Frères’ more daring lavender Early Grey. 4 out of 5 Blounts—Great. New Assam Chai This is the first tea from Postcard Teas that I like less the more I drink it. The culprit? The particular blend of spices: way too much green cardamon. Cardamon is a spice with a minty freshness that easily overpowers everything else, as it indeed does here. Also, the Assam is cut in finer pieces than I’d wish making this brew very dark and strong. I recognise the need for a strong tea to counterbalance the just as strong spices here but for me it was just a bit too much. Adding lemon and honey helps but only up to a point. I know you’re supposed to drop a few splashes of milk in it but I’m not British nor Indian so I don’t. 2 out of 5 Blounts—Mediocre. This is a traditional curled green tea from Japan called a “kamairicha” tea: instead of steaming the tea to stop the oxidation, kamairicha is roasted in a dry pan. Contrary to most Japanese teas such as Sencha, the typical bitter taste is gone because of this process. Mr Ogasa’s farm in Gokase is only 14 acres big. I’m not a huge Japanese tea expert but I do like this one. I do find it difficult to properly prepare: at more than the tea oxidises and still comes off as a bit too bitter. It’s more evenly balanced than the Senchas I have tried before, but that does mean it can come across as bland. I enjoy this tea the most when I am not doing anything else besides drinking tea. 3 out of 5 Blounts—Good. Miyazaki Oolong This complimentary little bag of Oolong tea leaves from Mr. Takuya Yokoyama tastes like a sweet Sencha instead of a typical Oolong tea. It’s one of the greenest ones with virtually no astringency, as described by Postcard Teas themselves. This is exceptional tea of which only was madein 2025. This is interesting because Oolong is usually made in China, not Japan. The problem is that this tea is very delicate: if you’re working or watching or playing something, you might gulp this down without blinking and afterwards think “what did I just drink?” I think these delicate teas are an acquired taste and require a mindful, peaceful moment of tea but nothing else. But why should I buy this Oolong when I already have the Guri Green? I usually prefer my Oolongs to be a bit more oxidised. I hope I’m not getting slammed for this. Oolong teas have a huge variety in roasting/oxidation/etc and this one ranges in the “barely Oolong at all” category. What I also learned is that for Oolong teas the first steep is usually a “wash” to get to the more flavourful second steeps. Perhaps I should try that for Miyazaki’s tea. 3 out of 5 Blounts—Good. Jasmine Green As mentioned on the postcard: “a delightful Vietnamese tea made with spring-picked green tea from Mr. Than’s tea co-op in the mountain village of Ban Lien in Lao Cai province”. Delightful is indeed the correct word here: this must be one of the best Jasmine teas I have ever tasted. It’s very delicate, never bitter, and after you’ve had a cup, you want to make another. What else can I say? It accepts but you better wait a few more minutes until it cooled down to at least and not let it steep for too long. Of course, our pantry now doesn’t stock the three Mariage Frères jasmine teas we tried, so I can’t directly compare them. They’re all great and completely different from the supermarket-bought Jasmine crap. 5 out of 5 Blounts—Amazing. Related topics: / tea / By Wouter Groeneveld on 29 June 2026.  Reply via email .

0 views
Anton Zhiyanov 2 weeks ago

Solod v0.2: Networking, new targets, friendlier interop

Solod ( So ) is a system-level language with Go syntax, zero runtime, and a familiar standard library. It's designed for two main audiences: The previous version (v0.1) focused on porting core Go stdlib packages and providing convenient C interop. At the end of that post, I said the next release would focus on networking, concurrency, or both. Now, networking is here — the v0.2 release I'm sharing today includes support for TCP, UDP, and Unix domain sockets. Concurrency is still planned for the future, so for now, servers handle one connection at a time. This release also lets you compile So to more targets, like 32-bit platforms, WebAssembly, and bare metal. And C interop even smoother! Networking • TCP server • TCP client • Deadlines • IP addresses • Targets • Interop • Stdlib • Wrapping up The main feature in v0.2 is the package. It's a simplified version of Go's package which supports the three most commonly used transports: The API mirrors Go closely, so most of it will feel familiar. The big difference is that So has no goroutines, so there's no concurrent server support — you accept and serve connections sequentially. More on that in a moment. Let's build a classic: an echo server that accepts a connection, reads a message, and sends it back. If you've written a TCP server in Go, this should look familiar — , an loop, and / on the connection. The only thing missing is a : without goroutines, each connection is handled to completion before moving on to the next . The client starts the connection using , then uses to send a request and to get the reply: UDP and Unix domain sockets work in a similar way. For UDP, an unconnected socket uses to get data and the sender's address, and to send a reply. For Unix sockets, there are (stream) and (datagram). By default, , , and are blocking. In Go, you'd typically use goroutines and contexts to prevent getting stuck forever. Since that's not available in So (yet), every connection and listener supports deadlines instead: , , and are available on , , , and listener types. When the deadline passes, any pending call fails with . If you don't set a deadline, a blocked call will wait forever. This isn't concurrency, but it's enough to keep a single-threaded server responsive. Along with , v0.2 ports Go's package, which provides small, allocation-free value types for IP addresses. represents an IP address, combines an IP address with a port, and is an IP with a prefix length (a CIDR block): These are simple value types that don't use any heap allocation, which fits well with So's explicit-memory approach. The package also provides and functions to help you work with strings. Solod compiles to plain C, which (in theory) means it can target anything a C compiler can. Because of this, v0.2 adds new targets: Here's the complete toolchain you need to build a freestanding binary using : A large part of the standard library ( , , , , , , , and more) works just fine in freestanding mode. For more details, check out the freestanding guide . A bunch of smaller changes make Solod nicer to write. Three new directives for low-level work, all documented in the interop guide : works with variables, constants, types, and functions. You can use it on multiple lines, and the attributes will stack. For example, will combine with . Type aliases . So now supports Go-style type aliases: Numeric C types . The package now includes named types for C's numeric types — , , , , , , and others. When you declare an extern function, you can use the actual C types in its signature instead of trying to guess the correct fixed-width Go type for your platform. Third-party packages . You can now add external So packages using or by vendoring, and you can organize your own code into multiple modules. So doesn't have a real package ecosystem yet, but it's a good start. Better diagnostics . By default, panic messages report the C file and line. Pass to report the original So source location instead: There's also an optional flag that adds nil-pointer checks when accessing struct fields and calling interface methods. This way, if there's a bad dereference, the program will panic cleanly instead of causing a segmentation fault. Both options are off by default to keep the generated code more readable. Beyond and , v0.2 adds a few more packages: And a small but handy update to memory management: now reclaims the last allocation if you give it the matching pointer. It's a minor optimization, but it means a quick alloc/free pair on an arena no longer wastes space. Stdlib documentation With v0.2, Solod has evolved from just "command-line tools and C glue" into something you can actually use on a network — like a TCP or UDP server, a small protocol client, or a Unix-socket daemon. The new targets (32-bit, WASM, freestanding) mean the same code can now run in more places, even down to bare metal. The big thing that's still missing is concurrency. A server that handles requests one at a time works for some tasks, but a real network service needs to manage many connections at once. That's the obvious goal for v0.3 — adding some kind of concurrency, along with the stdlib packages that support it. 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 and zero-cost C interop without having to learn Zig or Odin. C developers who like Go's style. TCP (networks , , ) via , , and , with the and types. UDP (networks , , ) via , (a connected socket), and (an unconnected socket with / ). Unix domain sockets ( for streams, for datagrams) via , , , and . 32-bit platforms . The compiler and stdlib now work correctly on 32-bit platforms, where and pointers are narrower. WebAssembly (WASI) . You can compile a So program to and run it under any WASI runtime. Freestanding mode . So programs can run on bare-metal systems without any C standard library. No libc means no malloc, but you can use instead. — hex encoding and decoding, including for hexdump-style output. — generating and parsing UUIDs (v4 and v7), with random components from a cryptographically secure source.

0 views
Stratechery 3 weeks ago

An Interview with Figma CEO Dylan Field About Design and AI

Good morning, This week’s Stratechery interview is with Figma co-founder and CEO Dylan Field . Field was a Thiel Fellow who dropped out of Brown in 2012 to start Figma. Figma was born of a technical breakthrough that leveraged WebGL to deliver powerful graphical capabilities in the browser; the browser made Figma collaborative, what I call the operating system of design . Figma has had a fascinating road: the company accepted an acquisition offer from Adobe in 2022, but due to regulatory resistence the latter was forced to abandon the merger in late 2023. Figma instead IPO’d in 2025 , and after skyrocketing to a valuation of $56.3 billion, has since crashed to a market cap of less than $10 billion, less than half of Adobe’s offer, thanks in large part to a market narrative that the company is an AI loser. I talk to Field about all of this, including his background, Figma’s differentiation discovery process, and the nature of creativity versus design. We get into the AI question, which the market views as a headwind, but which Field sees as a tailwind. To that end, the occasion for this interview was Figma’s Config conference and Field’s keynote where he explained how Figma’s Canvas was the natural intersection between design and AI. As a reminder, all Stratechery content, including interviews, is available as a podcast; click the link at the top of this email to add Stratechery to your podcast player. On to the Interview: This interview is lightly edited for clarity. Dylan Field, it feels like this interview has been in the works for years, but welcome to Stratechery. DF: Thank you, appreciate you having me, and big fan. Let’s start with your background. Where did you grow up, how did you become interested in technology? I always love these stories, especially the first time I talk to someone, and I think yours is a particularly interesting one. So give me the story. DF: I grew up in Penngrove, California, which is near Petaluma in Sonoma County — but not Sonoma, it’s critical to make sure people know where Penngrove is. My mom was an elementary school teacher, my dad a respiratory therapist, both not especially tech-savvy, but my mom early on realized that a computer would be useful for me to stop bugging them with questions and bug the computer instead. So I was lucky enough to get a — I think it was a Compaq Presario — when I was like five the family got one, and then I proceeded to really hog it. I’ve pretty much been interested in technology as far back as I can remember, I was very eager and excited to learn how to program, but didn’t necessarily have the ability to get my hands in a compiler for a while. It took until I got through some scholastic program, a BASIC compiler, to actually get properly started. I’ve also always had a, maybe not as much ability as I’d like, but a deep fascination with mathematics and just really everything in the world. And so this is just a fascination with the technology — like, how does this thing actually work, and how can I make it do what I want? DF: It was always more about product and design and about what technology will look like in the future and how to get there, rather than “I can really master the technology and have it under my control”, that was never really my vibe. What were the sorts of things you imagined you wanted to make as a kid, when you have this computer you want to figure out? DF: Walking around as a kid I was probably thinking less about the computer and more about, “Why can’t I teleport?”, or, on the flip side, going to SFO the first time and seeing they had these magical faucets where you put your hand in front and the water comes out and you didn’t have to touch anything — and I was a germaphobic kid — I’m like, “Why can’t the entire bathroom be automated?”, it’s just so obvious. Or, before I even learned how to properly read and write, “Why can’t I talk to the computer?”, stuff like that was more what I was excited by. Are you encouraged or discouraged by the progression of bathroom technology over the years? DF: Encouraged. Toto ‘s wonderful. Yes! It’s funny, because Toto is in the news because they make a certain sort of ceramic that’s used for AI stuff. I’m like, “Look, I’ve known about and been a Toto fan and supporter for many, many years”. DF: (laughing) I didn’t know that. Well, the other critical design invention here, which is very underappreciated, if you’re leaving a bathroom and you can use your foot to pull open the door, that is an underappreciated progression. Oh, there you go, that makes total sense, I can’t say I have that in my bathroom, but I do have a Toto Washlet toilet, they are well worth it — the only problem is you’ll be spoiled for life and won’t be able to live without it. So you end up at Brown — not what you’d think of as a technology school, it’s next door to RISD, which is a design school, so there’s an angle to where you ended up. What was the path to getting there, and the path to leaving as a Thiel Fellow ? DF: During high school I was probably a little overconfident, thought I could do anything and was beyond bright, and the world quickly proved me wrong, “Okay, there are people far smarter than you”. But due to that identity, I thought maybe MIT would be the place I want to go, then I toured MIT and it was a cloudy day, midterms, and I went, “No, this isn’t for me”, and looked at other spots. One person I’d talked with a lot was Danah Boyd — I met her through O’Reilly Media — and she was a really brilliant, thoughtful person, and she said, “You’ve really got to think about Brown”, and I kept randomly meeting Brown grads as I was doing this East Coast college tour, very randomly, and they’d all sit me down for an hour and tell me, “You’ve got to apply to Brown, and if you get in, you’ve got to go”. I ended up applying to Olin and Brown on the East Coast out of ten schools I visited, I was thorough, I didn’t get into Olin, which I thought was my first choice at the time. And then Brown, I was very surprised but thrilled to get in. What did you think you were going to study at that point? DF: Computer science and math, I did formally declare that as my concentration, but I didn’t get as far on the math side as I would have liked — did more CS classes, and also took advantage of Brown’s amazing open curriculum, where you can go very broad, I had some incredible classes in areas that are not technical at all. So where did the Thiel Fellowship come into the story? DF: It was the fall semester of my junior year. I was aware of the Thiel Fellowship — I’d seen it online, thought it was kind of a weird idea, but interesting. I got introduced to it by Elizabeth Stark , who now is, I believe, leading Lightning , she introduced me to one of the Thiel Fellows at the time, Dale. It was this weird one where he was 25 minutes late to a 30-minute meeting at Starbucks — we met for five minutes, but then he just kept texting me, “You’ve got to apply to the Thiel Fellowship”, very similar to the Brown story. I ended up applying after speaking with my now co-founder, Evan Wallace . Evan was the most brilliant person around — a year above me at Brown, my TA for multiple classes, and truly a genius, someone who’s also just fundamentally kind, humble, wonderful. I was like, “Man, I’ve done some internships now, there’s no one better to start a company with”, and if Evan were down for that instead of any number of jobs he can get when he graduates, I’d learn more from it than anything else — I can always go back to Brown, so I should at least explore it, and he surprisingly was down to explore it with me. So I applied to the Thiel Fellowship with a drones idea — which I think now is best being done by BRINC . Evan was just not down for that direction, he was down for WebGL and graphics, and I was psyched by that too, that’s the direction we headed. Tell me about the drones idea and the pivot to the WebGL angle, because it ties into the question I asked at the beginning — what were you pursuing? Was it the technology, or the end state? I think that’s an interesting through-line here. DF: I’ve always been excited about a lot of things — creation, creativity, design, even before I knew what to call design, which was most of my life at that point, I’d only recently learned what the word “design” meant, despite having done a lot of design. For me, I saw the act of starting a company was also about asking the question, “Why now?”, there are so many “Why now?” answers you can give, it can be societal change, cultural, technological, regulatory. But we were technologists at our core, so we made a big long list of all the technologies that were changing at the time and gradually crossed each one off, we came up with two finalists. One was drones, this is the end of 2011, the other one was WebGL. I think we would have totally failed at drones anyway, it’s extremely hard. You look at Zipline , BRINC — these are amazing companies, and you really have to chew glass to get through that, we wanted to do something where we felt we had a technological edge and insight others did not. And what was the technical edge and insight about WebGL? This is obviously the foundation of Figma — you can do incredible graphical things in the browser, which to that point had all been on dedicated desktop applications. What was the insight that made you think this might be possible, even if it was just barely possible? DF: To be clear, right after applying for the Thiel Fellowship with the drones idea, I ended up working at Flipboard as a design intern, using design programs all day long. We had this hammer with WebGL looking for a nail, we didn’t find the, “Let’s go build design environments and help designers”, for a while, it took a little bit. What was exciting was that Evan had done a lot of early work that proved out that WebGL was way more capable than anyone else was thinking at the time. Other folks then were going, “WebGL is this weird toy that Mozilla is making, it’s probably not as important as just using your local, non-browser tech”. Right, if you use an application that can actually leverage regular OpenGL and your GPU, why a browser? DF: Exactly. The only other company that seemed on to it at the time was Onshape , actually. We looked around and went, “These guys get it”, and pretty much no one else did yet, no one took it seriously. So due to Evan’s work, we started to really explore that and go, “How can we take tools that people expect to be desktop-bound and local, bring them to the browser, and do it collaboratively too?”. We were very inspired by Google Wave — rest in peace, it was a really cool product. I grew up in Google Docs, playing MMOs and stuff like that, so I think our frame of reference, even if we couldn’t articulate it then, was just different — obviously the browser enables all of that. You viewed the browser as a first-class operating environment in a way that probably older people did not. DF: Yeah, exactly. In the early days of Figma I’d say, “Just like Google Docs”, and a lot of people were like, “Yeah, well, I use Word — why would I use Google Docs?”, and I was like, “Well, I’ve only used Google Docs my entire life”. And then, “Well, I guess there was that time in middle school…”, and they’re going, “Wait, how young are you?”. Well, let’s talk about what Figma is. I’ve written about Figma in contrast to Sketch , which is more of a single-player experience — this idea that Adobe left this huge window open for actually designing apps. Mobile apps come along in particular, an exploding market, actually placing all the screens, how it all flows together, they didn’t have a product for that. Sketch comes in and fills that gap, but it’s still an application on your computer, and you’re saving files that are v1, v2, v5000. Figma, by virtue of being in the browser, got collaboration for free — it’s a multiplayer experience. When did that possibility become clear? You mention the collaboration aspects, but as I understand it, you were trying to get WebGL to work first, and then realized this is good for collaboration. Is that the right sequence, or did you have the benefit of being in the browser — meaning multiple people could work on something at the same time — all along? DF: I would say from day zero, Evan and I were talking about it, and we were both trying to be very rational. On collaboration, we wanted to talk with users and see, “Do they need it?”, and basically everyone said, “Not only do we not need it, we don’t want it”. Right, there was a lot of asking jockeys if they wanted cars. DF: Well, I think it was more an identity thing of, “I’m a designer”, and there was a lot of agency influence on the design process at that time — this kind of grand reveal where you just work in the corner. Oh yeah, you own it, it’s on your computer, you’re doing it, and then you go into the meeting and show it. DF: No one sees it until it’s perfectly ready, then you show a few results, maybe give them three, the first two are kind of not what you want, but the third, “Oh, the contrast is so great”, and everyone goes with it. So that agency mindset and identity, as well as imposter syndrome, honestly, because design was just emerging from this phase where people saw it as, “Make it pretty”, versus, “Make it work”. This is a key element of how we build product, build software, do media and advertising, and people were just starting to appreciate it with all the Apple ethos of the time and great consumer products coming out. So we had the insight from the start, but it took us a while. Eventually, as we built it out and started fully using Figma to build and design Figma, it was immediately clear there was no way we could launch without collaboration, because it just felt wrong. If you’re in Figma and I share a doc with you, a link, and you’re in it too, and I make a change and your browser force-reloads, and you make a change and my browser force-reloads, it sucks. So it was a, “We have to do this thing”, and it was not trivial at the time — it took quite a long time to build out. Evan was a key part of that, as he was with a lot of our foundational technology, it was a key condition for our launch in 2016. Is it ironic that Apple sort of created the conditions for you in raising the stature of design and that being the controlling factor in development, even as their whole tech approach is counter to you, not really supporting WebGL, being all-in on applications? It’s kind of interesting. DF: I don’t think Apple’s tech approach is counter to us at this point. At this point. But they were all-in on, “You use apps, that’s what they’re for”, this idea that you’re going to collaborate on the web — I’m not saying they hurt you, I’m just saying there’s a reason Figma only worked in Chrome for a long time, for example. DF: Apple reasonably was concerned about battery and device performance, and took a very vertical approach as they do with everything, and also was patient — just like we’re seeing now with them. When it became the right time, they added in collaboration to many other surfaces and figured out how to make it work with the cloud but I think they showed the importance of design to the world in a way that had never been so vocal before, and it raised the level of the conversation. You could argue Microsoft at the same point was also really leaning into design, but they weren’t as vocal — they didn’t have Steve Jobs talking about “Design, design, design”, they had “Developers, developers, developers”, it’s just a different tune. Yeah, that’s interesting. Is there any context, looking back now, where Figma makes sense for one person? Or is it really a product that only makes sense if you view it in this context of collaboration? DF: A ton of people that use Figma use it individually, and I think it’s critical that you build tools that work for someone individually, that they can then graduate into a collaborative stance and use with their team. But you have to get the single-player experience right and then let it evolve to multiplayer. So when you started going to market, what was your selling point? The tool itself, the accessibility, or was collaboration the key from the get-go? DF: When we first did our closed beta, multiplayer collaboration didn’t yet exist in the product. It did have sharing, and that was very powerful — you had this one space to view your designs with your team, and people were doing that in very team-oriented ways. But early on, things like our improvements on vectors, or the simplicity and quality of Figma, were more the differentiators — and then design systems with a unique component approach, and then multiplayer, and then many other things. We also got a lot of minimalists in our early user base — folks who believe in the cloud and believed in minimalism, because we didn’t have all the features. It was interesting just to see that early base of users and how successful they were — two of our earliest customers were Coda and Notion — just kind of wild that those were two of the first customers we had. I don’t even think Shishir [Mehrotra] at Coda knew that at the time — I once brought him in to talk with the team about platform strategy stuff, and I mentioned this offhand as an intro comment, and he’s like, “I was what?”, so it was a fun group to be around. How much do you think Figma has evolved with your customer base, as opposed to Figma actually influencing your customer base and how they evolve? Did your customer base naturally become collaborative and realize they needed Figma, or did Figma introduce them to working in a more collaborative manner that they hadn’t considered because the tools weren’t there? DF: There was definitely a period of adaptation, some people got it right away, for others it was over time. Our first big marketing moment — I remember there was a site, Designer News, sadly I think it’s offline now, and there was a comment on the launch thread, “If this is the future of design, I’m changing careers”, or someone said, “A camel is a horse designed by a committee”. But we went deep on anyone who had really positive or really negative sentiment around Figma — great, let’s learn from all of it and adapt as we need to, while also having our own points of view and pushing for them. Customers have always been inspiring to us, we’ve tried to take feedback from everywhere — support tickets, in-person conversations, formal research, sales, social media — for a while, social media was a great signal, it’s not as good a signal as it once was. Our user forums, everything, and data analytics. As you get there, you form a picture or view of the world, you play anthropologist and understand what people truly need and sometimes the moment just changes. FigJam , for example, was a product we introduced right after the pandemic started, I’d always wanted to make a whiteboarding and diagramming product — I saw that use case in the wild, it was significant, I felt we could make a simpler tool. But rightfully, the team was skeptical, always going, “Is this the right time? We have a lot of other stuff to do to make Figma great”, that debate stopped with the pandemic, when our user base wrote in en masse and said, “Please, please give us this product”. We need a whiteboard, yeah. DF: Yeah. We started seeing that use case everywhere — people treating Figma like a shared space and the shared-space part of Figma is something we’re doubling down on. Was that the real turning point, “This is where work is done”? I’ve called Figma the operating system of design , in that everything sits on top of it and below it, but it’s the common layer, does that resonate? Is that the moment that became much more real? DF: It was happening already in many ways, we were doing it ourselves, seeing it with our customers, but the pandemic is when everyone started telling us, vocally, “Lean into this”. There’s so much more that’s possible now as we bring more mediums to the Canvas , more expression to the Canvas, and let people truly get what’s in their heads onto one shared Canvas — to collaborate, but also riff, see a bird’s-eye view, and directly manipulate. AI is great, prompting is great, you should be able to do it in Figma — and you can now, with our agent , but you can’t filter all of creation through the lens of AI. If you have an idea, or many ideas in your head, you need to get them out directly too and also you have to iterate to get to an exploratory place. Too much emphasis right now is put on “I’m working with the AI, the AI wants to go a certain direction, and I’m going along with it”, it’s almost like, “Is the AI using you, or are you using the AI?” — sometimes it’s unclear. AI is a tool people can direct and work with, it can resolve tedium, but you also have to push, you have to be the out-of-distribution force, because AI is trained on the distribution, and the most interesting, differentiated work will be out of distribution by definition. So I have questions about that, I have questions about AI, and questions about Canvas, which is a big focus of what you’re talking about at Config this week. But I want to do a quick side tour, because I must, another very famous single-player design company, as I mentioned, is Adobe. The Adobe acquisition was announced in September 2022. I’d written — we don’t have to spend too much time on this, obviously it didn’t happen, so in some respects it’s not that important — but by that point— DF: Yeah, but it felt like it didn’t happen for a long time, those 16 months felt like an eternity. That’s right, which I do want to ask you about, get your point of view on. But one thing I’m curious about, I actually remember where I was when this happened, I’d written several times at that point about generative AI, particularly images , the AI question loomed very large to me when that news came out. But that was still a few months before ChatGPT had launched, so this was more burbling under the surface. To what extent was AI part of the Adobe conversation? There’s a very plausible story that it wasn’t part of the conversation at all — you were the operating system for design, the operating system can disintermediate all the products that sit on top of it, which from Adobe’s perspective was a strategic problem. They had a huge hole in this space, Sketch had already taken that whole space on the single-player level, so I thought it was an obvious acquisition for Adobe, aside from all the AI stuff, just looking backwards. Which interpretation is correct? DF: Probably both. I think Adobe was super excited about AI and understood its potential and importance, we had plenty of conversation about that, but it was not, I think, the impetus or driving factor for me though in making the call of, “Do we sell or not?”. I had no idea, would AI would 1/10th, or 10x, or 100x our business? I was in my head trying to play it all out, and as we’ve seen, it’s hard to play these things out. You kind of know what’s coming, but knowing when it’s coming, and the second-, third-, and fourth-order effects — that’s hard. And this is pre-ChatGPT, so imagine trying to play out the next five, six, seven years from that point, that made me much more receptive to a conversation. That makes total sense. For Adobe, I don’t think it was the controlling factor — again, you just made tons of strategic sense for them. But for you, it’s like, “$20 billion is very certain and everything else is very uncertain”, that makes a lot of sense. DF: Another contributing factor was that I was excited about the opportunity to think about Adobe’s Creative Suite from first principles, and go back to the user’s problems. Yeah — it’s missing the layer that Figma provides, the thing that actually ties it all together. DF: There’s so much expectation from users of any software that’s been around a long time. There’s a need that reinforces itself to “Add, add, add”, versus thinking, “Okay, we’ve learned a lot — how do we reinvent from the start and think about things in a new paradigm?”. Looking back now, AI is clearly going to be — and already is — a tailwind for our business, it’s TAM-expansive in huge ways I probably never anticipated at the time, it’s also interesting from the Adobe frame, because I’d challenge the way you framed it earlier. DF: Adobe acquired Macromedia , and through that got Fireworks — and Fireworks was really the predecessor to Figma and Sketch, but not a focus for Adobe. They had different Labs projects, but this was not their core, their core was creativity — for Figma, our core has always been design, those were different when the Adobe conversations were happening. Explain that, because I think I see what you’re saying, but people would usually conflate them — creativity and design. DF: The even bigger question, for the philosophers and art-theory folks, is, “What’s design?”, “What’s art?”, how do you differentiate design versus art? It’s muddy, but design has an aspect of problem-solving, it also has creativity. Art, I think, is a lot of things — you can get endless definitions of design and art — but I think of it as trying to take an emotion, idea, or concept and communicate it to someone in a way that really affects them. That’s not best framed as problem-solving, whereas design is. How about this definition: art is an expression that it’s meant to be consumed by the end user, and design is meant to serve the end user. DF: Well, I don’t even know if you should define art as being for an end user. Yeah, good point. DF: For me, one of the definitions I lean on is that design is where problem-solving meets creativity. Figma has always had people using the platform for creative use cases. But now you fast-forward to 2026, and design, creativity, media, in some ways art and in some ways not, and advertising — it’s all kind of merging together, it’s all one thing in a way I wouldn’t even have said in 2025. If you believe we’re in an attention economy — you experience this every day — and you believe you have to have a differentiated voice and really have a point of view in your work to stand out, and you think the way people judge software is the design, that’s the differentiator, but you also have to grab someone’s attention, design and brand are so connected. It’s all really coming together in such an interesting way, because of these second-order effects of more creation happening in the first place. A phrase you’ve mentioned, you said it earlier in this conversation, you’ve said it plenty of times elsewhere, is that AI draws from the middle of the distribution, and to be differentiated you need to be at the tails. That makes sense, but it’s funny because it conflicts with — go back to that user comment that’s deleted from the Internet, “Collaboration is the death of design”, do you see any tensions there? You talk about Adobe, creativity, tied to single-player, the genius of one person, versus, “We’re a group of people collaborating to get a design out the door”. How does that not end up in the middle of the distribution too? DF: It’s more of a mindset thing for any design team are they trying to do the safe thing, are they tryigng to go for the least common denominator where everyone agrees it’s a good idea? Or are they trying to be daring and bold and take risk? What we’re going to see over the coming years is the market rewarding the risk-takers. And I wouldn’t say it’s enough to be at the tail of the distribution — I think you have to be out of distribution. Is that possible? Aren’t you on the very edges of the tail? Fair enough. DF: I think every email I get from your mailing list is out of distribution. Well, thank you. I appreciate it. DF: If you can get one of the AI systems to replicate your judgment and framework-building, I would love to see it. I would both love to see it and hate to see it, so I guess it cuts both ways. DF: Sure, I might love to see it in terms of wanting to know how you did it. Well, it’s interesting for you, obviously. You mentioned a few minutes ago that AI is a tailwind for your business, I think it’s safe to say the stock market by and large does not agree with that, yet you’re there producing incredible results — you had a great quarter last quarter , your biggest beat yet. Do you feel you’re in the middle of trying to prove a negative here? What are the drivers of your business? Do you have some sympathy for the people in the market who are skeptical of you, or do they just not get it? DF: Markets typically have a narrative they’re attached to, and the narrative can shift — and maybe it’s still not the nuanced narrative that matters, but this happens all the time. Markets are so impressive as a force, and I just don’t think it’s worthwhile to try to argue with a market narrative. Are they normal distributions, and you’re trying to operate outside the distribution? DF: (laughing) I like that frame. I just think that you show up, you do great work, you focus on the inputs, you educate to make sure people understand, and eventually that’s either appreciated or not, depending on how the narrative is going. Right now the narrative is one of AI winners and AI losers, I don’t even think that’s nuanced enough, if I think more globally about software, there are many software companies and strategies that will work that are not necessarily companies and strategies that people would necessarily call AI winners today. I think about network effects. Are you a network effects business? DF: Collaboration definitely has properties similar to network effects, so in some ways, yes. And if you look at network effects not just in the social sense between people but also for marketplace liquidity — that is absolutely a network effect in itself, just to have liquidity in a marketplace, I would say that’s an AI winner. If you look at the long tail of customers that are non-technical — I invest in companies occasionally, and one of them is Ambrook , an accounting-for-farmers company. I don’t think a lot of people in ag [agriculture] will be vibe-coding their taxes, they’ll care very much to have a human in the loop, for the certainty that this part of their business is going well and they don’t have to worry about it. I really believe Ambrook can provide a phenomenal solution there. I also think liquidity of data matters — you need equity of data to create context, and context creates capability, if that’s self-reinforcing, you can get to a place where you have a virtuous flywheel that really helps in the age of AI. Explain this in the context of Figma specifically, why does this provide a tailwind for you? DF: I won’t go too deep, since it’s strategy, but the more activity people do in Figma, the more we can, with their permission, understand their needs and serve them better with capabilities. If we do that right, that’s a way to continually improve the experience for the customer and make it so they can do even better work, faster, in Figma. How are you thinking about the models that undergird your various AI offerings? DF: You always want to be in a place where models are swappable. We’re in an explosive, wild period of models constantly shipping, I went to bed last night and saw Sakana’s new release — I haven’t played with it yet, recording on Monday June 22nd just for reference. I didn’t expect that, coming out with their ultra model and their approach and just seeing the progress these labs are making, sometimes in a discontinuous way, is incredible. Right now we use a range of models and do some stuff first-party— And these would be based on open-weights models? DF: Some on open weights, some on very small things we’ve worked on. Overall, I think that there’s a big story around local inference that will happen in the future, as well as open weights and different models are good at different things, it’s incredible. Is it fair to step back and say — from your perspective, which echoes a Microsoft perspective , or lots of other companies in a similar position — yes, models have to be swappable, customers don’t want to be locked in, but there’s also a self-interest position, you need to keep this data to understand customers better, and you need to not be giving that data to the models, who at the frontier need to not be swappable. Do you feel they have no choice but to come up into your space? Is there a perspective where Claude Design comes out and it’s like, “Yeah, of course that’s coming, because they have to own the consumer”? DF: I think if you look at Anthropic right now — it echoes what we’ve seen from OpenAI over the past year, where there was a period when OpenAI was just building and releasing stuff in every area. And they, to their credit, have pivoted hard, made some hard calls, pulling back on Sora . That’s not an easy call after you do deals with major media players and have a huge launch and people are really enjoying the product, Sora was really cool, but going all in on code seems to be the right move for them right now, and it’s very respectable that they’re doing it. Anthropic’s going through a similar pattern, we’ll see what lasts and what ends up persisting. That’s an interesting way to think about it. Did you feel pretty betrayed about the design thing — particularly when one of their executives was on your board ? DF: It’s complicated. Let’s put it that way. Fair enough. I think it’s one of those things you could definitely see it coming. Tell me about Config. One of the products you’re going to announce is Code on the Canvas , tell me about that, and how it fits into the overall way you’re thinking about AI. DF: Maybe to frame it up to start and dispel some of the stuff out there in terms of the way people talk about this — people on social media love to frame the “versus”, they’re always talking about code versus design, like they’re two different things. To me, the work is not just vectors — it’s vectors, images, prototyping code, because you don’t always want to work in production, and production code, and production code needs to be across all your surfaces, web, desktop, all your mobile devices, new screen types, etc. All of that is relevant to your process, and all that process is design. So it’s super important to see it all as an “and” rather than a “versus”, I just want to make that clear because otherwise nothing else will make sense to folks. If you think about it as an “and” and go all the way into what that means, then basically what you end up with is, “How do you bring these different mediums, these different materials, together in one place where it’s easy to go back and forth and get the benefits of each?”. For design representations like vectors and images, I think there are many ways those are very helpful — especially vector-based formats, for direct manipulation and precise control, in ways that code, which is structured, is not as easy to manipulate and mold. But code is also incredible, it’s got expressivity, full fidelity, it acts the way it will in production — hopefully, a prototype might differ from production — and you can have state and logic but you’ve really got to bring these things together. So what we’re doing, based on the work we’ve done on Make , either from Make or by creating on the canvas yourself with code — essentially a code layer. You can have Code on the Canvas that pulls in from design if you want, and go right back to design — make changes and reconcile them back to code. We’re trying to make that all work seamlessly together, so you have a breadth of exploration while also having the collaborative aspects of the canvas and that bird’s-eye view. Is one way to think about this that the question is that you can you eat development before development tools eat you? DF: I think less that way, because my conceptualization of the moment we’re in is one that people are so eager to try so many different tools and materials — in some cases we’re going to be the best place to use those materials, in Figma, in other cases you’ll want to go elsewhere — and you might even want to come back to Figma afterward. I’ve been thinking about this, the vibe-coding stuff is amazing, particularly in its ability to build scaffolding and get the functionality of an app and the user experience these tools build is hilariously horrible — it’s so bad, you really have to put much more of a heavy hand on it. When you talk about a phrase you’ve been saying regularly — that when execution is cheap, design and creativity are the edge, that’s very resonant to me in that actually conveying properly to the AI what you want is still a difficult challenge without it over-interpreting and over-assuming and spitting out a UI that makes no sense, and the design’s not just wrong at a pixel level, it’s wrong at a conceptual level. I guess the question I have, and what I think you’re getting at with Code in the Canvas, correct me if I’m wrong — is that you guys owned the handoff between designers and developers where Figma was the common level where you could communicate back and forth, what’s happening, how it’s working. To some extent, if the developers are doomed, God bless them, designers rule the world — but did you accidentally erase your whole point of differentiation, which is owning that handoff between those two pieces? I don’t know if that makes sense, but it’s an angle I’ve been thinking about here. DF: I don’t think developers are doomed, and I do think designers will rule the world. (laughing) Both can be true! DF: But I need to go all the way back for a second, when we started Figma, the first five years or so in market, a big part of our story, but also the ecosystem around us, was prototyping. And prototyping was not always with code, some companies tried that approach, but it didn’t really work at the time, because despite all the debate of, “Should designers code?” — debates that happen every year or two on Design Twitter, we would constantly see that designers did not all want to learn or take the time to code. Now we’re in a world where it’s easier for designers to put their ideas into code. If you look at the prototyping aspect alone, in the Canvas, whether you’re working with production materials or prototyping, you need to be able to riff and explore and try things, and design representations are just one part of that, so is code. We’re also doing more launches at Config that add to that story. Motion, for example . Yep, huge focus on this. You bought Weavy now you’re calling it Weave . DF: Weavy, and now Weave, yeah. I love talking about Weave , it’s so cool. But Motion is actually coming from a hybrid of Figmates and a team we acquired called Modyfi . It’s something folks have always wanted — a timeline they can use in the Canvas and of course the challenge is how to do that in a way that doesn’t get in your way if you’re not trying to do Motion work. I think we’ve done a great job balancing those tradeoffs while providing a really powerful motion tool that’s much more intuitive than other approaches of the past and it’ll allow people go far more into expression, because it’s very hard to prompt and say, “I want the curve of the animation to be exactly like this”, the work we’re seeing folks do, even internally, with this motion tool is so incredible — I’m just totally wowed. We’re also going hard on shaders , going all the way back to the WebGL conversation. It’s ironic, we were built with shaders all this time, but we didn’t give people using Figma the power to express in shaders. Now you can add shader fills and effects, and that unlocks a parametric option space to really explore this whole universe of effects, images, fills, and properties — and that’s even before interactive shaders, which add a whole new dimension, that’ll come soon. We’re excited to bring all these materials to the Canvas so people can fully express and explore. And yes, if we do it right, it’ll be something they can then push to production — whether that’s pulling from Figma via an MCP , or more in the future, connecting to your codebase. We’re doing that with Make local right now, but we have much more to prove out there. I’m curious about that, because how do you think about customer acquisition? Back in the day you’d imagine starting, “Oh, Figma, this tool I’ve heard about, I’m going to make a design, and now I’m going to find a developer to code it”, now people can just get started with a ChatGPT or a Claude, and then it’s like, “Oh, this is really hard to design UI elements”, how do I back into something? How do you make sure you’re there if people are starting with coding in a way they maybe didn’t previously? DF: I see people starting everywhere — that includes Figma, but also all sorts of other tools and places, and I see them ending everywhere. I see them ending in Figma to do the final iteration, ending in LLMs or other services. What I think is essential for us right now is providing enough value always that the path to a great product is through Figma. Yes, optimally you can do that entire path through Figma as well, that’s a standard we should hold ourselves to. But we’ll continue to see people use a range of tools for a while, because these models are so underexplored. If we were to pause all development on models, a total moratorium, I think you’ve got like five years of catch-up on the application layer before the capabilities are understood and expressed through software. Every time I use these models, I find new capabilities. Even there, though, is still the key for Figma is that it’s still the place people can work together? And that’s something AI hasn’t really solved , it’s kind of a one-on-one experience, but you need to figure out how groups can get jobs done. DF: One area is groups working together to converge, I think groups coming together to diverge is also really important. Teams being able to work in all sorts of ways in the future is critical and also what are the things you’re always going to want as a team that are fixed, and what are your degrees of freedom? There’s so much we can lean into on collaboration in ways we’ve never been able to before, and make that single-player experience even better — because if we land all that together, you’ve got the collaborative layer, but also Figma is the place where you can just make anything you want. That sort of leads to my question, which is, is the real Figma danger not that AI becomes multiplayer, but that individuals with AI disrupt multiplayer companies? And that’s why you still have to be relevant to the individual as well. DF: I think it’s kind of a dark future if that happens, it’s one where folks are probably feeling pretty lonely — it’s also one where the tunnel vision you have when you’re building with AI is really becoming a problem for teams, I’m hearing this from design leaders everywhere. There are different phases of AI adoption at these companies, the first phase is often, “We’ve got to use AI, let’s figure that out”, the second is like token-maxxing leaderboards — some extreme behaviors. The third, after they get people to adopt, is often “Okay, here’s your token budget”. In that second phase especially, where people go really wild with AI, it’s hard to get them to change their behavior after. A lot of people have this total tunnel vision of, “I’m building this one thing”, and they get really attached to it. That’s the opposite of the breadth of what a great design process offers. If you’re going through the design process, it’s not that you should slow down necessarily, but you should go broader, and you should think. It’s essential that you actually think — not just wear a thinking cap, you need to be able work through yourself and have a mental model not only of the user and the experience you’re creating, but also cultural impacts, the broader system you exist in, what the user is expecting, all sorts of things. Going fast in the wrong direction is not progress, it’s a dead end, and it’s even worse if you’re collaborating, trying to bring five designers together and each one is viscerally attached to their one direction — now you’ve got design gridlock and you’re talking past each other. So it’s imperative that we move away from this tunnel vision and toward the openness the Canvas represents. Maybe there are other ways too, but we’ve got to get away from tunnel vision. On a personal level, how much do you feel constrained by the path dependency of having already built Figma? If you started out tinkering with tech as a kid, or even with the WebGL stuff, you ended up with a company. Do you ever have a part of you that’s like, “I’d just like to tinker with this tech again and not worry about whether it’s an existential crisis for this huge company I built”? DF: I’m constantly tinkering. It’s my antidote to the non-verifiability of design — because there are verifiable domains and non-verifiable domains. Design is taste, culture, aesthetic, it’s constantly shifting, user experience is something designers can argue about in design crit for as many hours as you give them. Unverifiability is the moat — that’s a good metric. The more something’s been argued about on the Internet, the longer a future it probably has. DF: (laughing) The more you’re oriented toward questions than answers, I think it’s a good sign — it’s going to be harder for models to achieve it in a way that’s high-craft. And as a builder of Figma, that’s where the complexity and the interesting parts lie. The word of the year — not just this year, but 2025 as well — is evals, evals, evals. But how do you write the right evals for non-verifiability? Aren’t evals, in some respects, counter to taste? DF: Depends on how you do them, and who’s writing them, there are ways. It’s hard for LLMs to do well on aesthetics and user experience, like you said, and being surrounded by non-verifiability — when I go home and I’m finally unwinding at 11 o’clock, about to go to sleep, I’m not reaching for Netflix, I’m reaching for some model, and I’m exploring verifiable tasks, actually. Because I want to push the models on the unverifiable side we talked about all day long, but what can we do where it’s really verifiable and they have spiking capabilities? Like vibe-mathing, for example, which oddly creates empathy for our vibe-coders. Because I vibe-math, and as someone who never went as far as I wanted to in pure math and wasn’t as good as others, I don’t know all the concepts the LLMs might be spitting out at me, so I have to learn as fast as I can — which is not fast enough, because the LLM is going through all sorts of stuff. It’s a great tool for learning, and super fun for discovery. And looking at the internals of models, how they work, understanding what you can and can’t determine, is also extremely interesting. It’s all applicable in weird ways to Figma — you never know how. Even early stuff I did around understanding how to get models to have a broader range of outputs, and prompting strategies, I don’t think there’s one definition of the word “jailbreak”, but the things that got the models to open up more, exploring that direction, has really led me to understand models better, which benefits Figma in weird ways. It’s super interesting. We didn’t get too much into the aftermath of Adobe, or the IPO, that sort of thing — but you talk about unverifiability and uncertainty, and that’s been the Figma story often, through things outside your control. It’s been interesting to observe, it really is quite an adventure of a company in many respects, really a unicorn. DF: It’s been a blast, continues to be, and with the world shifting quickly, you can see it as chaos, or as opportunity — or both. Are you glad you’re independent, or do you kind of wish… DF: Oh, at this moment I’m very glad to be independent, we need to operate at such a speed and be able to pivot so quickly to make sure we update our priors. Like the opposite of how you started, right? You started out with a two-year slog to even get this working. DF: Totally. It’s so important now to constantly adjust as an org and make sure our processes support that, there are tons of things to do to improve there. But when people come to Config — which will be, as of the time this is released, I think happened yesterday, time’s weird on podcasts — I’m so excited. It’s going to be 10,000 designers in one place, and I get to spend time with the community and show them the stuff we’ve been working on. I think they’re going to love it and there’s tons more we’re working on, so stay tuned. Very good. Dylan Field, nice to talk to you. DF: Thank you for having me. This Daily Update Interview is also available as a podcast. To receive it in your podcast player, visit Stratechery . The Daily Update is intended for a single recipient, but occasional forwarding is totally fine! If you would like to order multiple subscriptions for your team with a group discount (minimum 5), please contact me directly. Thanks for being a supporter, and have a great day!

1 views
Takuya Matsuyama 1 months ago

I made a Claude Code session manager for tmux

Hi, it's Takuya. I'm happy to introduce a tool for managing multiple Claude Code sessions in tmux. Here is a demo video: First, let me share a bit about the journey that led me to build it. Or, you can just jump right to the repo here: Recently, I've read Christoph Nakazawa's blog post . He emphasizes the importance of the toolchain in boosting feedback loops and developer productivity to work efficiently with coding agents: It inspired me to focus on updating my tool setup and DX in my project. First, I focused on improving tool performance. For example, I recently migrated Inkdrop's build toolchain from webpack + Grunt to (Vite 8 + Rolldown). It made the production build 10x faster, and the dev build now launches almost instantly. Also, I installed , a Go-based rewrite of TypeScript. It's super fast and has improved my AI development pipeline, because the AI often runs a typecheck on every task. Similarly, I migrated my linter from to , and my formatter from to . Next, the review process. I've been using the lazygit integration of snacks in Neovim. When I review changes across a lot of files, I use Yanuo's codediff.nvim . Since I often jump around files, I added an option to automatically open the diff when changing the selection in the explorer without pressing Enter (it got merged into codediff.nvim . Thanks, Yanuo🙏). These improvements have been great for boosting feedback loops in a single project (bonus: without ever leaving the terminal screen or touching the mouse). As Christoph says, working with coding agents is like working in a large organization. It means you have a bunch of engineers to manage. The next pain point in my workflow is managing multiple Claude Code sessions. I usually run multiple Claude sessions simultaneously, because I have a lot of modules and libraries to maintain, e.g., the desktop app, mobile app, theme module, markdown renderer, React Native libraries, etc. I shared a tmux tip on how to run Claude Code in a tmux popup window with persistent sessions . It allows me to have fewer tmux windows instead of separate windows for each Claude Code session. But it's been annoying to check whether any sessions are finished or need my answers by switching windows and opening each session in a popup window. So, I created a tool to manage Claude Code sessions in tmux. I published it as a tpm plugin so you can quickly try it. It gives me a single picker over all my running Claude sessions, so I can see which ones need me and jump straight to them instead of switching windows. In a nutshell, it supports: It's built entirely on tmux primitives and shell scripts — there's no background daemon or extra process to keep alive. Here's the gist: Because everything is stored on the tmux sessions themselves, the state survives detaching, reattaching, and closing the picker — there's no separate database to get out of sync. It's a tpm plugin, so installing it is one line in your : Then hit + I to install. You'll also need fzf (it powers the picker UI) and the CLI — both of which you probably already have. The whole workflow comes down to two keybindings: (Both keys are configurable — see the README if and are already taken in your config.) This is the part I built the tool for. Hit + from anywhere and you get a list of all your running Claude sessions, each with a colored status dot: The sessions that need you (waiting and idle) float to the top, so a glance tells you where to go next. On the right is a live preview of each session's screen, so you can see what Claude is actually doing without leaving the picker. From there: Out of the box, the picker lists, previews, jumps, and kills — but the status dots stay until you wire up Claude Code hooks . The status is the best part, so it's worth the two minutes. The hooks stamp each session's state onto its tmux session as Claude works: You just add a small block to — the exact snippet is in the README . Claude Code picks up hooks dynamically, so there's no restart needed; your running sessions start reporting status on their next event. That's the whole thing: + to start sessions, + to see who needs you, to jump in. That's it. Hope it is useful for your terminal workflow with coding agents! Enjoy AI coding. 🔢 A central picker ( + ) listing every running Claude session. 🟢 Live status per session — / / — driven by Claude Code hooks, so you instantly see which need you. 👁️ A live preview of each session's screen right in the picker. 🎯 Smart jump — selecting a session switches your client to the window it was launched from, then resumes it in a popup over it. 🚀 A launcher ( + ) that opens/attaches a Claude session for the current directory. ❌ Quick kill ( ) of finished sessions from the picker. Each session is a plain tmux session. When you launch one, the plugin starts a detached session named running . Because the name is derived from the path, launching again from the same directory just re-attaches to the existing session instead of spawning a duplicate. State lives on the session itself. The Claude Code hooks stamp a option ( / / ) onto the tmux session whenever Claude changes state. Nothing polls in the background — the status is written the moment it changes. The picker is fzf. It lists every session, reads each one's state for the status dot, and shells out to for the live preview. When you pick one, it switches your client to the window you originally launched it from (remembered in ) and resumes the session in the popup. + — launch. Spins up (or re-attaches to) a Claude session for whatever directory your current pane is in, and drops you straight into it in a popup. Run it once per project, and you've got one session per repo, each named after its path. + — the picker. Opens an fzf popup listing every Claude session you've launched. 🔴 working — busy, leave it alone 🟡 waiting — needs your input (a permission prompt or a question) 🟢 idle — finished its turn, your move jumps to the highlighted session. It switches your client back to the window you originally launched it from, then resumes the session in a popup right there — so you land back in its context, not some random window. kills the highlighted session — handy for clearing out finished ones. Type to filter, / to move around. Standard fzf.

0 views
André Arko 1 months ago

<code>rv</code> plan and progress update

This post was originally given as a talk at Rubycon IT 2026 . The slides are also available. It’s been a while since I first talked about , a Ruby manager for the future . I’d like give an update on what we’ve done since then, but I’m going to recap some of that earlier post first to give context for the updates. If you still remember what I said back then, you can jump to the new stuff right away . Either way, I’m excited to update you about the work that we’ve been doing, and show exactly how far we’ve gotten. For the last ten years or so of working on Bundler, I’ve had a wish rattling around: I want a bigger, better dependency manager. It doesn’t just manage your gems, it manages your ruby versions, too. It doesn’t just manage your ruby versions, it installs pre-compiled rubies so you don’t have to wait for ruby to compile from source over and over. And more than all of that, it makes it completely trivial to run any script or tool written in ruby, even if that script or tool needs a different ruby and gems than your application does. For the entire ten years of daydreaming, I’ve been hoping someone else would build it and I could just use it. Then I discovered that someone did build it… but for Python. It’s called . In August 2024, uv version 0.3 shipped, and it had all the features I had wished for, and even more that I hadn’t thought to wish for. At this point, I’ve been using for almost a year and every time I use a project written in Python, the experience is delightful. Not only can you run a command directly out of packages that aren’t even installed, you can run a command that requires a Python version you don’t even have installed. takes care of installing the right python, installing the right packages, and running your command, in just a second or two. Whether you want to run a CLI tool, a webapp, or a random script, always ensures the environment is correct as part of running the command. Need Python? Installed. Need a package? Also installed. Never again run on a new package, only to realize later you broke something old. No more setting up dependencies manually, only to discover later that the script stopped working inside cron while you weren’t checking on it. Last year, my long time consulting job disappeared and I found myself looking for something to replace it. One of my ideas was to start a company inspired by Geomys in the Go language, offering expert advice from open source maintainers, but the idea felt weak to me without a “spotlight” project to show off our expertise. In July of this year, I finally realized that these two ideas could go together extremely well—our company can show our expertise by building this developer tool, and clients paying for our advice to solve their problems can ensure we are able to support and expand the tool. I talked to some Ruby friends about the idea, and it resonated with them, so we started working on both the company and the open source project. Today, Spinel Cooperative has a website at spinel.coop , and has a website at rv.dev . The team has expanded, and now includes David Rodriguez , the former lead developer of RubyGems and Bundler, as well as former Rails core team members Kasper Timm Hanson and Sam Stephenson . Sam has even done some of this work before, as the original creator of and the tool. Our goal for is to be a new kind of developer tool. You don’t need to install and then pick a Ruby version, install it, and then update RubyGems and Bundler, and then your gems. Instead, you just run the project command you care about, and everything is handled. It’s a version manager, and a dependency manager, and more than both of those things. With that vision in place, we were faced with a very practical question: what can we build that would be useful right away? After some prototyping and a lot of discussion, we landed on precompiled rubies for development work as the most useful place to start, and got to work. After deciding what our first feature would be, we had to pick a language to use. We landed on Rust to build , for two main reasons. The obvious reason is that Rust produces very fast results, and that seems to also be why is written in Rust. The less obvious reason is based on years of trying to onboard new contributors to Bundler and RubyGems—it turns out if you are a Ruby developer, you unfortunately don’t (yet) know the subset of Ruby that we have been forced to use to write Bundler and RubyGems. There are two major things that basically every Ruby program does that you can’t do if you are managing gems. First, you can’t use any gems. If you want to use code that’s inside a gem, you need to copy that code wholesale into Bundler or RubyGems, and then you need to constantly update it anytime that gem has any changes. Second, you can’t use anything with native extensions, ever. JSON gem? Psych gem for YAML? Completely impossible, because Bundler and RubyGems need to be installable even if there is no compiler present. So with those constraints in mind, and with our goal set to “a tool so fast you normally can’t even tell it’s running”, we settled on Rust, and started building a CLI. I’ve used Rust for smaller personal projects in the past, but I had never created a full CLI tool. I am happy to report that the library for creating CLIs in Rust is great, and I recommend it to anyone interested in Rust CLIs. The next piece that we needed was the actual precompiled Rubies themselves. To install Ruby quickly, we needed to be able to skip over the dance. There are a couple of big projects out there compiling Ruby in advance, but they are mostly for use on servers. The GitHub action, and the official Ruby docker images are both based on the project originally started as part of . Unfortunately, those aren’t usable for our needs because they aren’t statically compiled and relocatable . Statically compiled (as opposed to dynamically compiled) means that Ruby copies the code from a shared library into its own binary. Now for small aside (but it’s relevant, I promise). Have you ever had trouble compiling Ruby because of OpenSSL? I’m pretty sure every Ruby developer has. Have you ever had an already-installed Ruby suddenly stop working because of OpenSSL, and you had to install it again? That also seems extremely common, thanks to Homebrew’s aggressive auto-update policy. The good news is, fixes both of those problems. By putting OpenSSL inside the Ruby binary, they can never get separated, and those errors can never occur. There is a tradeoff here—if there is a critical security flaw in OpenSSL, we will need to compile Ruby again to include the critical security update. The first reason we are okay with this tradeoff is that OpenSSL doesn’t have huge security issues very often. The second reason we are okay with this is that your production servers are probably using the official Ruby docker images and not Ruby installed by , so it’s even less of a concern. In the end, the closest existing system we were able find was Homebrew’s project. That’s how Homebrew builds the Ruby install that Homebrew itself runs on. The Homebrew team built some excellent infrastructure for building a statically linked Ruby, including libyaml, openssl, and other required libraries. The big thing Homebrew did not do was build more than one single version of Ruby, or support YJIT. We’ll come back to that in a bit. The part of is about builds being relocatable. Since Homebrew needs to be able to install into on x86, but on Apple Silicon, and into any user’s home directory for Linuxbrew, they need to be able to take a single precompiled Ruby and put it in any location on disk. That’s another one of the requirements that isn’t met by the or Docker image Rubies—if you move them to another directory, they stop working. Using Homebrew’s as a base, we were able to start with macOS ARM and Ubuntu x86, add Ubuntu on ARM, and then build every version in the Ruby 3.4.x series. Once we had those ready, then we asked ourselves: how much tooling do we need before this is useful for developers? Just linking to a repo with Ruby binaries in it isn’t really that helpful, because if it’s harder to use than running , it’s not really a better or faster experience. We landed on a small set of useful features for the first version: the latest Ruby minor version, 3.4, built for macOS ARM and Linux x86, with support for files, and automatic Ruby version switching just in zsh. After a few weeks of work, could switch between installed Ruby versions in zsh, but most importantly it could install precompiled Ruby on macOS and Ubuntu in one second flat. Yes, you heard that right. . Wait 1 second. Done. You can run Ruby commands now. With that functionality in place, we released version 0.1. Immediately after our initial release, we were hit with an extremely nice surprise: someone from the Homebrew core team decided to add directly to homebrew-core within a few days of 0.1 being released. That makes it much easier to install and try it out, and completely removes any need for us to create and maintain our own custom homebrew tap, which is a very nice bonus. With proof our concept working and users installing v0.1, we immediately started to expand the core functionality. We added support for bash, fish, and nushell. We spent several weeks working through the issues involved in compiling every single point release of Ruby 3.3 and 3.4. Then we spent another two weeks working through all of the issues compiling all of those Rubies with YJIT turned on. Then we spent another two weeks working through the issues of compiling all of those Rubies for macOS on x86, and for Linux on ARM. Once all of those Ruby versions were available, we shipped version 0.2. Building on our progress with Ruby versions, we added more versions of Ruby: every 3.2.x version, and all of the 4.0 prereleases and final releases. After hearing from and users who wanted to re-use their file, we added support for that file as well. Automatic Ruby switching will respect files, and will update the version written into the file if it exists. As a fun easter egg, we also added a precompiled binary of the oldest version of Ruby with published source code, 0.49. All of those features shipped as version 0.3. At that point, we took a break to take stock of the project, our goals, and our plan. 0.3 is a pretty good Ruby version manager, and a viable option in the pantheon of Ruby version managers like , , or . While precompiled Ruby is great, we want superfast installs for not just Ruby but also all gems and bundles. But Bundler is huge! It took three of us a year to build originally, and has had 15 years of additions by dozens of contributors. We can’t build everything we want in a month, or even three. After much brainstorming and discussion, we made a plan to deliver real-world useful tools that would build on each other, so we can work our way up to a complete application dependency management tool. First, we would need to understand gems themselves, parsing the compact index of gem metadata and then reading gemspecs and .gem files. Then we would need to install gems, not just copy files into the right places but also running the steps to compile native extensions correctly. Once we can install gems into the right places, we need to parse the format to install bundles. Then we need to build a resolver, the process that transforms a into a by taking a list of gems and producing a graph of dependencies that are all compatible with each other. With that plan, we got back to work. The first feature from that plan was , which does the same thing as . This is the same thing that you use when you’re running your tests in CI, or that you use when you’re deploying your application to a server. As long as you haven’t made any changes to your Gemfile, we can read the lockfile, install all of your gems, and set up the environment so that your application is able to run. To build this, we implemented a compact index client, gemspec parsing, native gem extension compilation, and gem installation. And it works! Starting with 0.4, you can clone a project, install your gems, and run the project. The next release included a small sidequest to add Windows and PowerShell support, as well as compiling Ruby binaries against musl libc so they will work on Alpine Linux. We use the precompiled binaries for Windows produced by the ruby-installer project, which turns out to be the only existing project that precompiles Ruby. This release also included the next two steps of our incremental plan: first, automatically managing Ruby version and installation. If you , you don’t even need to have Ruby installed, will make sure that happens if needed. The second part was the next step of our gem management plan, taking a list of gems and resolving dependencies to install. When combined, those two features unlock uv-style “tools”, where a gem CLI can also have an auto-managed Ruby version. Have you ever used to get a CLI tool only to find out later your Ruby version changed and broke the CLI? tools completely prevent that problem. With tool support, we could then add gem auto-install to create . Run any gem command, even if it’s not installed! With version 0.5, you can go straight from to a Rails app from in 10 seconds flat. At the SF Ruby conference late last year, a random conversation with Kokubun, the ruby-core member and maintainer of YJIT and ZJIT spawned an idea: what about testing against the latest Ruby? The Ruby version managers that compile Ruby onto your own machine handle this by adding a version of Ruby named “dev” that just means “check out the ruby git repo and compile the newest commit”. It was only a few days of effort to get the ruby compiler handling ruby from git, but it was a few weeks of experimenting before figuring out how to handle a “version” that keeps the same name but changes every day. It was worth it, though, because now you can install and test against the latest daily Ruby build as easily and as often as you want, without ever waiting for Ruby to compile. It’s not quite finished yet, but the next step in our incremental plan is to handle the same responsibilities that the command handles: evaluate the Gemfile, resolve the graph of gem versions, update the Gemfile.lock if needed, and install all of those gems. When I was learning about uv, this part absolutely blew my mind because is so fast that it runs as part of every command! Coming from Bundler, that was completely incredible. I could not imagine running before every because that would make everything so, so slow. It’s very exciting to work toward that for Ruby. That’s not all we have planned, either. The roadmap includes project setup and task management, making it easy to run scripts or other commands with your Ruby and gems available. Managing gems for scripts means adding a config file as a comment inside the ruby script file, with the Gemfile-like information needed to install gems. can then auto-install those gems in order to run the script. It’s not yet clear how long it will take to finish this initial list, even after it’s done we have a ton of additional ideas. As we wind things up, I want to show off a couple of things that I personally think are the best and coolest uses of rv. this isn’t necessarily the stuff that you’ll do the most often, which is fine, but these examples are super impressive to me, coming from the nightmare of ruby version building. First up, : once you have , you don’t need to think about Ruby, you don’t need to think about gems, you just run the command that you want to run, immediately. is fast enough that you can start on a machine with no Ruby installed, run , and be generating that app in less than 10 seconds. One command to install Ruby, install Rails, install all 60 gems that Rails depends on, and run the command you originally wanted. It’s just an incredibly delightful experience to not need to think about Ruby versions or gem dependencies when you want to run something. Another thing that has come extremely in handy is the ability to write scripts across Ruby versions, and know those scripts will work whether or not those Ruby versions are installed when the script runs. You don’t need to care about installing Ruby, or even checking for Ruby at all. Just run the command you want to run and will take care of all that stuff. Finally, the commands (inspired by ) allow you to use CLIs without having to think about Ruby versions, or global gems, or bundled gems, or what application directory you are in. Tools always get the Ruby version and the gems that they need to work, regardless of your currently chosen Ruby version and app and gems. For me, has unlocked the ability to use Ruby CLI tools again, and I love that power and flexibility. In the end, we want to live in a future where anyone can run a Ruby command, or tool, or application in a few seconds (or less!). We’re building that future for ourselves, and we welcome everyone else. Visit rv.dev to see the project on GitHub and give it a try! We’d love to have your help building it.

0 views
Unsung 1 months ago

The trouble with font previews

A reader sent me this screenshot from PowerPoint, with one of the menus looking the best it’s ever looked, and the other one showing to work with what we could charitably call “a UI hangover”: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/1.1600w.avif" type="image/avif"> It’s obviously bad craft and crossing over to the “embarrassing” territory, but I thought it’s an interesting question: what happened? The main piece of the puzzle is that the first menu shows the name of the font in San Francisco, but the second asks to render the font name in itself, serving as a font preview. = 3x)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/2.1600w.avif" type="image/avif"> Font previews are fascinating because they are the perfect showcase of how tricky fonts can be at scale. Some time ago, I wrote an essay called Typography is impossible . TL; DR: It’s actually impossible to left align or center text. Ever. Not just because each font does whatever it wants – font size is a number that doesn’t really give you anything to hang a hat on, and the font can place itself in its box however it desires, too – and not just because fonts often lie (via bad metrics) about what they store inside, but also because aligning and centering are really in the eye of the license holder, and have more than one definition. So, every time you align text to anything, in whatever way, it’s only an approximation . Most of the time that’s good enough. Here it is not. I worked on font previews at Figma, and wanted to show you three screenshots of what we did. This first one shows the default attempt: we ask the fonts to render themselves in the same size (16px), vertically centered in a box that’s always 28px tall… and they oblige on paper, but it really doesn’t feel like they are: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/3.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/4.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/4.1600w.avif" type="image/avif"> The second take shows what happens if you nudge the fonts up and down so they’re aligned to their baselines. This at least creates vertical rhythm; in effect, we need to make the fonts uneven to make them feel even. = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/5.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/5.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/6.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/6.1600w.avif" type="image/avif"> And this is the final result, with extra adjustments: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/7.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/7.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/8.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/8.1600w.avif" type="image/avif"> What do we do in the final version? Too many small things to mention, but in essence: These adjustments are all in the same category: getting off math balance to get to optical balance. Here, you can compare before (the naïve version) with after (the final version): = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/9.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/9.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/10.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/10.1600w.avif" type="image/avif"> If it feels subtle, imagine it applied to a much wilder menagerie of very thin, very huge, or very strange fonts. (The go-to example? Open a Mac and try typing in Zapfino .) I’m not showing this to brag about my work – okay, fine, to some extent I am, we’re all human – and I truly believe this could be so much better, still. There are icon fonts, color fonts, and non-Western fonts so rich in variety and tradition that this category itself is basically a fractal. Mostly, I wanted to share this lesson: dealing with fonts is hard, and dealing with fonts as a system even more so. Whether it’s the printing press, paper, or Illustrator, it takes people years or even decades to fully learn the craft of setting type, and to believe their eyes instead of only relying on math. But here, what’s needed is manufactured craft : we have to teach the machine to trust its eyes (which it doesn’t have) over math (which it can’t escape). Now if you’re wondering why font previews look bad in so many apps, I believe it’s because people working on those did not allocate enough time to deal with all that. But I’ve used the word “embarrassing” as there’s one more thing that the original did poorly, and something the reader identified immediately. The makers of PowerPoint allowed the font to escape its containment: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/11.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-trouble-with-font-previews/11.1600w.avif" type="image/avif"> This is another big lesson: fonts will ignore their bounds at every single opportunity. That infamous CSS IS AWESOME graphic? That’s CSS underestimating text. That naked URL or code snippet pushing the mobile site past the viewport and making it scroll? That’s the creators of the site not building up enough imagination of what fonts can do when they’re not watching. Zalgo text ? A joke, but based in reality. Fonts are so much more feral than you think. Are you ready for it? Thank you to Giovanni Lanzani for sending in the original PowerPoint screenshots. #details #typography We literally measure the fonts (programmatically) by rendering them and looking at them, and make adjustments. We blow them up (but not too much) if they’re optically too small, or reduce them (but not too much) if they’re too big. We have a multiplier for scripty fonts and monospace fonts, where the traditional measurements are likely to be off. We even special-case specific fonts by name. That feels like bad practice, but fonts are so varied and all over the place, that I think it’s perfectly fine to make exceptions for particular individual fonts that are popular or otherwise very important to your users.

0 views
Sean Goedecke 1 months ago

Working with product managers

The relationship engineers have with product management is more dysfunctional than with any other part of the company. There’s no shared culture or language like there is with other engineers, and the rules of “who gets to tell who what to do” aren’t as clear-cut as they are with managers. Engineers don’t have a lot in common with legal, or design, or sales, but they also don’t need to interact much with those roles. In my experience, engineers are communicating with product managers almost every single day. The worst version of the product/engineering relationship goes something like this: Engineers are technically competent but are too autistic to be fully trusted. They need a kind-but-stern parental figure who knows how to communicate to other stakeholders in the organization (for instance, by being comfortable using the word “stakeholders”), and how to keep engineers from going off in the wrong direction. This entire gross dynamic is neatly captured by the popular term “product mommy” 1 . I really, really don’t like that term, or this entire dynamic in general. Almost none of my relationships with my product managers have been anything like this, though I have seen it at a distance. Working well with product managers can be the difference between succeeding and failing at a company. Why is it so hard to maintain good relationships between engineering and product? What does a good relationship look like? Product managers and engineers have largely non-overlapping skillsets. Product managers don’t understand the technical work engineers do and aren’t equipped to talk about it: if an engineer gives a technical reason for something, product managers generally have to shrug and say “sure, I guess”. Likewise, engineers don’t have anything like the visibility into the organization that product managers do. Particularly in large organizations, it is the product manager who is the source of truth about who wants what and which features are important. When a product manager says that something is critical, engineers generally have to shrug and say “sure, I guess”. This obviously requires a lot of trust. What’s a little less obvious is that this trust is continually broken by both sides . Every single product manager has been told thousands of times that technical task X is technically impossible or would be disastrous, only for that task to end up being done fairly smoothly and successfully. Every single engineer has been told thousands of times that requirement X is absolutely critical and worth going to enormous effort for, only for that requirement to be silently dropped or changed with no apology. Of course this isn’t malicious. Engineers often give wrong estimates because estimation is impossible , and sometimes the dire consequences they warn about really do happen (they’re just handled behind the scenes, like engineers handle many other kinds of technical dysfunction). Product managers “change their minds” because what’s important in a large tech company does genuinely change hour-by-hour 2 , and even the best attempts to only filter the most reliable priorities through to the engineering team will sometimes go wrong. The consequence of this broken trust is that the relationship becomes very difficult to maintain. When you’re an engineer, and you explain something to your product manager, and you know they don’t believe you (despite having no ability themselves to judge the question), it can be incredibly frustrating. Likewise, when you’re a product manager, and you’re desperately trying to explain what we need to do to an engineer, and you know they’re internally shrugging their shoulders, it must be unbearable. Don’t they know this is critical to the company? You were just in a meeting with the leaders of the organization! The natural tool for a mistrustful product manager is manipulation . I still remember a product manager who tried to extract a commitment from my team by asking us to go around and all say “I commit to getting this work done in two weeks”, after a conversation where we’d explained the risks that cause it to take longer. I suppose the idea was that we’d all work much harder, having taken a sacred oath? More subtle variants of this approach involve suggesting that you would be really disappointed if this work was delayed (in true “product mommy” style), or vaguely suggesting the possibility of some abstract reward (that the product manager is not empowered to deliver) if work gets done ahead of schedule. The natural tool for a mistrustful engineer is lies . The most benign version of this is exaggerating estimates: for instance, the classic advice to double your estimate and add 20% . I’ve seen engineers claim that they’ve had to follow up on all sorts of largely-fake tasks (one common example is “reaching out to a neighbor team to confirm X”) in order to gain more time. In the worst case, engineers might even straight-out lie that work has been completed, and then track the “it doesn’t work in production” feedback as a bug. Once this starts happening, it’s nearly impossible to repair the relationship. I can’t bring myself to trust a product manager who’s clearly trying to pull my strings, and I’m sure a product manager can’t trust an engineer who’s lied to their face in the past. That’s why it’s so important to avoid getting into a bad relationship in the first place. Why bother? If it’s so hard to hammer out a good working relationship with product managers, why not just settle for a bad one? Product managers can absolutely bury you if you’re not careful. Product managers are almost always more politically sophisticated than engineers. This is partly structural: product managers are simply in more conversations with the company’s movers and shakers, and so naturally have a better relationship with them (and are thus better attuned to which way the wind is blowing). It’s also partly selection bias: engineers can be hired even with relatively poor social skills, because they’re primarily being assessed on technical ability, but social skills are a core part of the product role 3 . If you are feuding with a product manager, you will probably lose . Unless you’re unusually influential, they will simply have far more opportunities to quietly talk you down in influential circles than you will. All it takes is a few comments like “oh, I probably wouldn’t pick Sean for that project” to wreck your reputation. In the case where you are openly feuding with a product manager, the company’s leaders will by default take the product manager’s side over yours. They’re likely to know them better, have more shared cultural context with them, and in general be willing to interpret the situation as “another engineer who doesn’t understand how the organization works”. There are huge benefits to being trusted by a product manager. Product managers want to ship things , and typically understand a fair amount about all of the non-technical barriers to shipping. If you also want to ship things, you can become a fearsome team. On top of that, because trust between engineers and product managers is so difficult, once you’re in you’re in all the way. Product managers often pick one or two engineers as their go-to for getting the “real story” on technical questions. If that’s you, you have an outsized position of influence in the organization, which you can use to get the things you want done . As an engineer, how can you build trust with your product manager? The first step is to understand where they’re coming from . When they tell you something is important or that a requirement has come in, be aware that this is rarely their decision. It’s not them who’s jerking you around, it’s someone higher up in the food chain jerking you both around. If you can adopt a conspiratorial mindset with them, instead of against them, that’s a good start. Try just asking “oh man, alright, what can we do about this?” instead of complaining. The second step is to be right, a lot . This is a silly-sounding Amazon leadership principle that turns out to be entirely accurate. I wrote more about it here , but (as unfair as it sounds) you really do have to be mostly accurate if you want to build trust with a product manager. When you say something will ship, it has to ship; when you say something is impossible, it can’t happen days or weeks later. It’s okay to be wrong sometimes , but you have to establish a pattern of you providing them useful, correct technical information. The third step is to let them make the political calls most of the time . If you expect them to trust your technical calls, you have to extend them the same trust when it comes to navigating the organization. Don’t publicly undermine them in meetings, bring up your concerns in private. If they say something is important and you’re not so sure, at least act like it is. Accept that sometimes they’re going to be wrong, just like you’re sometimes wrong about technical questions. The fourth step is to get lucky . Sometimes your product manager will just be a dud. You can’t build trust with someone incompetent: there’s nothing for you to trust them with, and they aren’t in a position where they can usefully extend trust to you. Working in large organizations requires getting comfortable with the fact that some of your colleagues will be stronger than others, and figuring out ways to work with (or bypass) people who make the work harder, not easier. Many product managers were once engineers. If your product manager is technical, does that make you immune from these problems? Absolutely not! You likely won’t have much choice in which product managers you work with, but be aware that having once been an engineer is a negative , not a positive. No product manager can ever be technical enough to matter, because they don’t work on the codebase : even if they were a full-time engineer, they wouldn’t have the time to build the specific context on the system they’d need to be a real participant in technical discussions. It’s thus better to have a product manager who knows they’re not technical than to have one who mistakenly thinks they might be. The worst-case scenario is an ex-engineering product manager who believes they’re technical enough to detect when engineers are lying to them. This kind of paranoia is an easy trap for “technical” product managers to fall into, particularly when they don’t have a trusted engineer on the team they can lean on. If you’re dealing with one of these, prepare to spend a lot of time explaining why you can’t “just” do things (and prepare to have those explanations not be believed). At its worst, a product manager relationship is like an unhealthy family: driven by condescension, emotional manipulation, lies, and mistrust. This isn’t because product managers are bad people! It’s because the structure of the relationship creates conflict. Both sides must make commitments (about the technical system or goals of the organization) that are (a) often wrong, and that (b) the other side is unable to independently verify. To avoid the trap, both sides have to be generous, willing to trust each other in their areas of expertise, and most importantly competent . Unlike most roles in tech, product management (particularly the lower-level roles that are more engineer-facing) has close to an even gender split. For instance, based on the whims (or snap decisions, more charitably) of the CEO. I have worked with product managers with poor social skills, but it’s rare: about as rare as working with engineers with genuinely poor (i.e. by general-population standards) technical skills. Unlike most roles in tech, product management (particularly the lower-level roles that are more engineer-facing) has close to an even gender split. ↩ For instance, based on the whims (or snap decisions, more charitably) of the CEO. ↩ I have worked with product managers with poor social skills, but it’s rare: about as rare as working with engineers with genuinely poor (i.e. by general-population standards) technical skills. ↩

0 views

Thoughts on starting new projects with LLM agents

A few months ago I wrote about using LLM agents to help restructuring one of my Python projects . It's worth beginning by saying that the rewrite has been successful by all reasonable measures; I've been able to continue maintaining that project since then without an issue. In this post, I want to discuss another project I've recently completed with significant help from agents: watgo . In this project many things are different; most notably, it's a from-scratch project rather than a rewrite, and it uses a different programming language (Go). This post describes my experience working on the project, and some lessons learned along the way. This is a new project, so it required extensive design. I began by iterating on the design with the agent, with a sketch of the API. For this purpose, I recommend using a Markdown file committed into the repository for future reference. After that, I started asking the agent to write CLs [1] in a logical order that made sense to me, keeping them small and reviewable (more on this in the next section). Sometimes it's not easy to have a small CL, and multiple rounds of revision may confuse the agent; in this case, I commit the CL and then go back and ask the agent to modify or refactor the code, as much as needed, with separate CLs. In the worst case, the whole sequence can be reverted if I feel we've taken the wrong direction (branches could also be helpful here for more complicated scenarios). This point is worth reiterating: sometimes a single CL is a huge step forward, but requires lots of review, cleanup and refactoring to be viable. I've had multiple instances where an agent produced several days of work in a single CL, but I then spent hours instructing it to clean up and refactor. Overall, it's still a productivity gain, just not as much as some pundits would like us to believe. Given the current state of agent capabilities, I think it's worth splitting projects into two categories: The watgo projects is a clear example of (2): I certainly intend to maintain this project in the long term, so I insist on code that I understand. With very few exceptions, no code gets in without full review and often multiple rounds of revisions. Even if the cost for writing code went down, maintaining a project is so much more than that. It's triaging and fixing bugs, it's thinking through what needs to be done rather than how to do it, it's keeping the code healthy over time, and so on. As Brian Kernighan said : Maybe at some point agents will become good enough that projects in category (2) can be implemented and maintained completely autonomously. Maybe. But we're certainly not there yet. My hunch is that getting there will require crossing the AGI line [2] , after which little in our world remains certain. If you're using an agent to send an actual PR and only review that , it's difficult to be disciplined enough to actually perform a thorough review. I find the following method to be more reliable: I use a CLI agent running locally in my repository, and ask it to update the code there. In parallel, I have a VSCode window open in the same project, where I can: Once I'm pleased with the change, I manually create a commit. As mentioned above, it's imperative to keep making progress in small chunks, with small enough CLs that a human can fully understand in a single review. It's very tempting to sprint ahead submitting thousands of lines of code every day, but this temptation has to be avoided. Coding with an agent is like speed-reading; yes, you're making more progress, but comprehension suffers the faster you go. Particularly for refactoring, agents still take the shortest route to destination. It's important to guide them to think about the "big picture" at all times, find all instances where X is better done as Y, not just a single place noticed during a review. This is why it's sometimes OK to have a CL submitted before you fully agree with everything, and go back to it later for several refactoring rounds. Source control works amazingly well when pair-coding with agents. It's a key point discussed in every "how to succeed with AI" article, but still critical enough to reiterate here: a solid testing strategy is absolutely crucial for success. Agents produce - by far - the best results when they have a solid test suite to test their code against. With the pycparser rewrite, I had a large existing test suite. For watgo , the very first thing I did was think through how to adapt the test suites of the WASM spec and of the wabt project for my needs. If your project doesn't have such tests to rely on, this should be your first order of business - finding one, or building one from scratch. Beware of self-reinforcing loops though; it's dangerous to trust agents for both the tests and the implementations tested against them. Go is a fantastic language for agents to write, because it's designed to be very readable by humans. The biggest strengths of Go are exactly what makes the experience of reviewing agent code so positive: Since most of the time spent by humans when using agents is reading rather than writing code, these effects compound and produce a great experience. Recall the discussion of how some languages are optimized for writability (Perl) while others are optimized for readability (Go)? Well, when working on a project with an agent we live in a world of 99% reading vs. 1% writing, so this really matters. I find this aspect really crucial in light of the earlier points made in this post - namely, keeping the human in the loop by understanding and reviewing all of the agent's design choices and code. If you're working on a subject that's completely new to you, I would strongly recommend against the approach described in this post. To really learn something, you have to work through it from scratch, yourself, reading, designing, writing the code. Agents don't change this basic fact; even before agents, if you wanted to learn X, copying it from Stack Overflow or some other project clearly wasn't the right way to go. Similarly, while agents can be used as a prop for learning, they cannot learn for you . As a corollary, junior engineers should exercise extreme caution when relying on LLMs. There's no replacement to hard-won experience and the sweat and tears of learning new, challenging topics. Learning is supposed to be hard; if it's too easy, you're probably not learning. For senior engineers, agents are a boon; it's a great tool to increase productivity, avoid the boring stuff, and get unstuck from procrastination; but only when used judiciously. Low importance / prototype / throw away projects where deep code understanding is unnecessary. These can be "vibe-coded" (submitting agent code without even reviewing it). High importance projects that I actually want to maintain; here, vibe-coding is ill advised and I insist on reviewing and guiding all code the agent writes before it's submitted (or shortly after, as discussed above). Review the agent's changes using VSCode's diff view Make my own tweaks and code changes if needed Go changes very infrequently, so you don't have to wonder "are we using the most modern / idiomatic approach" or "what the hell is this construct" as often as with other languages (looking at you, Python and TypeScript). There are relatively few ways to accomplish the same thing in Go, further lowering the mental burden. The standard library is rich and there's much less need to keep abreast of the package-everyone-uses du jour. In general, Go is designed for readability, with a mild-but-still-strong type system, uniform formatting, explicit error propagation and opinionated choices already made for you.

0 views
iDiallo 1 months ago

Why all the PRs?

It's a signal. That's why we get AI-generated PRs. We told everyone, in order to get your resume taken seriously, you need to show your work. When I was getting started in my career, that meant having your own website that you contribute to regularly. So I did that. I built websites, I maintained them. I kept maintaining them even after I got the jobs because that's how I actually honed my web programming skills. Where else was I going to try new frameworks, a new JavaScript paradigm, or try out Ruby on rails? I got the job, and I advised other developers to follow the same path. But then github became mainstream. Rather than just show a finished website, you could actually share the code that runs your project. Share a link to your github project and companies can review your code and directly gauge your experience. But even better, you can show your contribution to open source projects. Not just any projects. Popular projects. The github stars became a metric people look for. A signal that can be used to quickly assign a value to a candidate. But that’s the story told from the outside. I don’t think the github profile link was ever important, unless it was significantly good. Employees focused on their work rarely have the time to maintain healthy github activity. Their experience comes from their day to day job. So for the most part, not much attention was placed on github links other than skimming through those surface level details. When stacks of resumes came on my desk, the best candidates stood out because they had work experience. The good candidates had projects that they could link to, github or elsewhere. But then, the worst candidates had long padded resumes that had elements of every job application tips-and-tricks-article. They had a website, but it was built in a day for the purpose of getting a job, with nothing interesting to say. They had github links, but those often pointed to school projects, homework, or boilerplate code. That’s the vast majority of github links I used to get. People with active and well maintained github profiles were rare. Rare because it actually requires time, effort, and experience. But then we have AI. There was a golang auth issue that I've contributed to on github. It was already a few years old when I proposed a solution that worked for my case. It wasn't universal so it wasn't accepted. The discussion is revived every couple years, each person bringing one more piece to the puzzle. But then recently, someone exploded the thread with comments. And even created a PR to go with it. This was from a user that went from a dormant account to 4000 contributions in a year. It was all AI assisted code. This isn’t to comment on the quality of his code, but he was clearly trying to optimize the metric. Looking at his linkedin profile, he doesn’t work in a software engineering role, and it’s hard to decide if he would be a good contributor if hired. If we were to judge his resume by looking at the github profile, it might catch our attention. But then, there is a problem. There are hundreds, even thousands of people all doing the same thing. They are cranking up their contributions to github projects using AI, so they can have a better chance at getting hired as developers. I understand the job market is rough right now, especially for gen z , and anything to differentiate yourself is a plus. The problem is this is being done at the expense of open source projects. The contributors are not submitting PRs to your project because they are personally invested in it. Instead, they are trying to get their name on the contributors list so that they can use it as a signal in their resume. When we are out here debating if there is any merit in AI generated PRs, or if we should just judge the code, we tend to miss that their gesture is completely hollow. The PR’s author intentions are completely misaligned with the project's maintainers. They are playing a different game. We call it slop, or a waste of time, we ban them and they get really vocal about expressing their first amendment rights. We are directly interfering with their goal of padding their resume. I often ask, why don’t people who create those PRs not just start their own project? One answer I’m starting to believe is, nobody cares about a github profile with a handful of stars. You need to contribute to a popular project. Most if not all AI generated websites look the same, it doesn’t matter how well you customize the prompt. Most greenfield projects from new programmers look the same, the prompter lacks the experience to do anything different. Contributing to open source is a scary thing when you are new. Even when you have experience, it’s a deliberate act. You have to be invested in the work. Just like asking questions on stackoverflow, issues you raised will often get closed . And when they do, you have to learn from it. The value of an open source contributor is not in the volume of work they can perform. If you skim any important projects, you’ll see that the best contributors spend more time discussing the problem than writing code. Their value is in solving problems and contributing to the collective memory of the group. But when you are doing a drive-by PR that may or may not be correct, and you are just trying to get your name on a list, you are providing zero value to the maintainer. Just more work. This is the signal every slop PR generator is after.

0 views
Xe Iaso 1 months ago

IPv6 zones in URLs are a mistake

IPv6 is weird. One of the more strange parts of the standard is that every interface's link local addresses are in . If you have a machine with two network interfaces, both of them will be in , so if you have a packet destined to , how do you disambiguate it? The answer is you use IPv6 scopes/zones . The exact format of what goes into a zone is OS dependent, but on Linux it's the interface name and on Windows it's the interface ID. This lets the kernel's routing table know how to handle an address range conflict. On my tower, this would be represented like this: Where is the name of my tower's ethernet device. When you create a host:port bindhost, you normally separate the hostname and port with a colon. IPv6 uses colons to separate hex groups. In order to disambiguate what's the host and what's the port, you typically format the IPv6 address in square brackets, so on port 80 would look like this: And with the right scope it looks like this: Now let's get URL encoding into the mix. From high orbit, you can imagine a URL's format as being something like this: An IPv6 zone would then be part of the hostname, just like with that port 80 example from earlier. So you'd think the URL would be something like this: But if you try to parse this as a URL in Go, you get an error: This happens because URLs can't represent all Unicode values, so any values that don't fit into the grammar of a URL become percent-encoded . This is why sometimes you'll see a in URLs in the wild; that's encoding the ascii space key, which is invalid in URLs. In order to work around this, you need to percent-encode the percent sign in the IPv6 zone: In theory, there is guidance for how to properly handle IPv6 zones in user interfaces in RFC 9844 , but there's no such guidance for URLs . Go also does not seem to follow this RFC in net/url . EDIT: It seems that this behaviour is compliant with RFC 6874 and that this is in fact how it is meant to be done. Our industry confounds me. So in the meantime in order for Anubis to point to IPv6 zoned addresses, you need to encode the with percent encoding. This is horrible, but it seems that this is an edge case that applies to other frameworks, programming languages, and libraries: Maybe some day in the future there will be a better option here. In the meantime my policy of not forking the Go standard library means that this somewhat terrible UX for an edge case is acceptable. I hate it, but what can you do? TL;DR: computers were a mistake. https://trac.nginx.org/nginx/ticket/623 https://github.com/psf/requests/issues/6808 https://datatracker.ietf.org/doc/html/draft-schinazi-httpbis-link-local-uri-bcp-03 -- Browsers don't currently support IPv6 zones because it breaks the concept of an "origin" which is used for many subtle things, this RFC draft attempts to define an zone origin in IPv6 so that browsers have a leg to stand on

0 views
Corrode 1 months ago

Veo

I don’t know about you, but to me there are few things as interesting as the hardware/software interface: the point where carefully written code meets the messy, physical world of sensors, lenses, and real-time constraints. It’s where a clever abstraction either holds up or falls apart the moment a real signal hits it. That makes Veo a perfect guest. The Copenhagen-based company builds AI-powered cameras that record and analyze sports matches, from grassroots football pitches to professional clubs, and then turn hours of raw footage into something coaches and players can actually use: automatic highlights, player tracking, and match analysis. To get there, they have to capture panoramic video on a custom camera, follow the action without an operator, and crunch an enormous amount of data, reliably and at scale. My guests sit on both sides of that interface. Anders Hellerup Madsen works close to the metal on the camera itself, on the embedded firmware and the GStreamer media pipeline that turns raw sensor data into video. Gorm Casper works further up the stack, on the backend that ingests, processes, and analyzes those matches in Rust. Together we talk about where Rust fits across that whole journey, the trade-offs of doing media and computer vision work in a systems language, and what convinced a sports-tech company to bet on Rust for the parts that absolutely cannot fall over. CodeCrafters helps you become proficient in Rust by building real-world, production-grade projects. Learn hands-on by creating your own shell, HTTP server, Redis, Kafka, Git, SQLite, or DNS service from scratch. Start for free today and enjoy 40% off any paid plan by using this link . Veo (Veo Technologies) is a Danish sports-tech company, headquartered in Copenhagen, that builds AI-powered cameras and a video platform for recording and analyzing matches. Instead of relying on a human camera operator, a Veo camera captures the entire pitch in panoramic video and uses computer vision to automatically follow the ball, generate highlights, and produce analysis that coaches, players, and clubs can use. What started in football has grown into a platform used by tens of thousands of teams across the world, spanning many sports, from amateur clubs to professional organizations. Anders Hellerup Madsen is a Senior Software Engineer at Veo, where he works on embedded firmware and on the GStreamer -based media processing pipeline that runs on the Veo camera. He is also a GStreamer contributor. Gorm Casper is a Software Engineer at Veo. After many years working on the frontend, he now spends his time on the backend, writing Rust. He holds a Master’s in Digital Design & Communication from the IT University of Copenhagen. GStreamer - The open-source multimedia framework at the heart of Veo’s camera pipeline gstreamer-rs - The Rust bindings for GStreamer OpenCV - The open-source computer vision library Nvidia Jetson - Like a Raspberry Pi, but with more video processing capabilities glib - The foundation of gstreamer, also of GTK, Gnome, and many more ffmpeg - An easier video manipulation tool, but without good support for custom pipeline elements CUDA - Nvidia’s tooling to run C++ code on the GPU Sebastian Dröge - Amazing Rust and GStreamer developer OCaml - A really nice language and an inspiration for Rust Rustonomicon - The dark arts of unsafe Rust Latest Announcement from Nvidia - CUDA for Rust - Nvidia’s experimental Rust-to-CUDA compiler, cuda-oxide Rust GPU - Write and run GPU code in Rust, announced on 2026-05-12 Temporal - A durable workflow engine Rust in Production: Astral - The Python company that does uv and ruff, with Rust serde_json::Value - The Rust analogue to Python’s dict ReasonML - OCaml with a better syntax bedquilt - Write 80s Text Adventures with Rust Rust Book: Transfer Data Between Threads with Message Passing - The chapter explaining the Go motto “Do not communicate by sharing memory; instead, share memory by communicating” Veo Website Anders Hellerup Madsen on LinkedIn Anders Hellerup Madsen on GitLab (freedesktop) Gorm Casper’s Website Gorm Casper on LinkedIn Gorm Casper on GitHub

0 views
Sean Goedecke 1 months ago

Anti-AI nostalgia and the cult of the past

Programmers were better back in the day, weren’t they? Back when we had real programmers. Not just people who got paid to write code, but people who lived it, who were obsessed with their craft, and whose code was a lively expression of themselves. Hackers were hackers in those days before money took over the industry. Don’t even get me started on LLMs. Could there be a better example of today’s degenerate spirit? A machine to mass-produce software (not good software, just barely good enough), so that the weak minds that dominate the industry can indulge their obsession with quantity : of slop code, of features, and ultimately of money, which is the only way they can understand value. If they weren’t destroying our way of life, they would be pitiable. All of them together don’t have a fraction of the spiritual integrity of someone like Mel . But as it is, we must band together to crush them and drive them from our industry like the parasites they are. Okay, that’s not actually what I believe. But there sure are a lot of posts 1 and comments on the internet that sound a bit like the paragraph above. Here are some older quotes that might sound similar: …the third collapse, in which power tends to pass into the hands of the lowest of the traditional castes, the caste of the beasts of burden and the standardized individuals. The result of this transfer of power was a reduction of horizon and value to the plane of matter, the machine, and the reign of quantity. 2 Usura rusteth the chisel \ It rusteth the craft and the craftsman \ It gnaweth the thread in the loom 3 The actual accomplishments of the past will nevertheless remain accomplishments, while the artistic stammerings of the painting, music, sculpture, and architecture produced by these types of charlatans will one day be nothing but proof of the magnitude of a nation’s downfall. 4 These are all from the writings (or speeches) of famous fascists: Julius Evola, Ezra Pound, and Hitler himself. Mussolini’s Doctrine of Fascism begins by defining fascism as a “spiritual attitude”, which the fascist man adopts in order to regain the mysterious qualities that were lost by the transition to modern life. In his classic Ur-Fascism , Umberto Eco’s first two defining features of fascism are the “cult of tradition” and the “rejection of modernism”. So when someone tells me that the industry has lost its way and we must deny the corrupting influence of modern technology in order to retvrn to the time of virile real programmers (who understood and appreciated the spiritual dimension of programming), I get suspicious. It’s strange to describe anti-AI sentiment as potentially fascist, since a very popular argument is that LLMs themselves are an inherently fascist tool. Surely both sides of the debate can’t be fascist? I do think that the structure of fascist arguments is generally persuasive , and that many avowedly anti-fascist groups do sometimes fall into this trap: describing the world as a struggle between the spiritual power of the macho, traditional man and the corrupting influence of degenerate (often foreign) capital. For instance, I am a big fan of Lord of the Rings. I’ve read the series and watched the films multiple times, and even made a failed attempt to learn Elvish as a kid. But it’s hard to deny that fascists absolutely love Lord of the Rings. “Marble statue of a Roman emperor” might be the most popular avatar for fascists on the internet, but Aragorn is the second most popular. Neo-fascist movements in Italy explicitly take up Lord of the Rings as a foundational text. Why? Because the core conflict in the text is between the traditional, nostalgic heroism of the Shire and Gondor, and the corrupting modern industrial (partly foreign ) influence of Saruman and Sauron 5 . I don’t think Lord of the Rings (or anti-AI rhetoric) is intrinsically fascist. In fact, the surface-level reading of the text is anti-fascist: the plucky people of the West banding together to fight Sauron’s command-and-control totalitarian society. But I can see why fascists love it. One common historical touch-point for anti-AI folks is the Luddites, who were a violent conservative labor movement in early 1800s England. Anti-AI blogs adopt Luddite language like “smashing frames”, and positively cite the Luddites as “the go-to enemies of fascism since its inception”. I’ve written at length about what we can learn from the Luddites in Luddites and burning down AI datacenters , but one point I think is under-emphasized by the (generally pro-Luddite) books is that the Luddites were a little bit fascist themselves . Brian Merchant’s Blood in the Machine is the most popular recent book on the Luddites. I enjoyed it, but Merchant’s attempts to paint the Luddites as a friendly, left-wing, proto-feminist movement 6 seemed really unconvincing to me. From the writings of the Luddites, it’s clear that they were interested in protecting the rights of their all-male elite guild fraternity. Here’s one Luddite threat to a workshop that explicitly includes a threat against the female workers 7 : We think it quite inconsistent with our duty as men, as husbands and as fathers to suffer ourselves to be ruined any longer by a set of vagabond strumpets and those gibbet-deserving rascals that are looking over them. We will lead them to their satisfaction. We sincerely hope, gentlemen, that you will discharge the bitches and take men into your employ again, or they must take what they get. These were fundamentally conservative people who felt (correctly) that modernity had deprived them of their elite status, handing it instead to lower-paid inferiors: women, vagabonds, and foreigners. The Luddites were obviously not fascists 8 . However, the basic ingredients were there: wounded pride, a masculine elite identity, hatred of modern economics, and violence aimed at restoring their previous position in society. The currents that produced Luddism are the same currents that guided so many unhappy people towards fascism. When things are looking grim for an elite group, they often turn towards any movement that promises a return to an idealized past. If my blog has themes, one of them is surely that many software engineers labor under a delusion that their job is to be excellent at their craft. Of course, wanting to be an excellent programmer is not a delusion; it is a completely legitimate value to hold, and a legitimate purpose to pursue. It’s just not what you’re paid to do at work. Your job , unfortunately, is producing shareholder value . This delusion has been punctured by the end of ZIRP , and again more recently by the rise of AI coding. In this environment, I worry that some software engineers will form exactly the kind of disillusioned elite that was the audience for Ezra Pound’s poems about “usury” or the Luddites’ campaign against unapprenticed (often female) textile workers. I worry that AI, and the companies that build AI, are becoming an enemy against which anything is permitted: an enemy which in Umberto Eco’s words is “at the same time too strong and too weak”, unable to reason and yet powerful enough to drastically reshape the global labor market for the worse. The enemy of fascism is nuance. Fascism presents a good, clean, rousing story about a spiritual conflict between right and wrong. It is anathema to fascism to stop and muddy the waters a bit: in this case, to explore the ways in which LLMs, like any transformative technology, can both support and endanger traditional values. In The left-wing case for AI I wrote about how AI is being used right now as a disability aid, and many disabled readers wrote in to share their positive experiences with LLMs, and often how alienated they feel by the anti-AI mainstream on the left. I recently got an email describing how there’s a sudden flood of accessibility software for blind people 9 that’s actually built by blind people , who can now iterate with a LLM to get a product that meets their needs. Framing AI as an ontological evil erases experiences like these. Being anti-AI is not inherently fascist. Many of the anti-AI posts I’ve quoted are thoughtful, sensitive pieces exploring how the author thinks about one of the biggest changes to our industry. I still think the world needs more articles like that, not less, but the more of them I read, the more I recognize the tropes: spiritually pure lovers of the craft, degenerate peddlers of corrupt modernism, a need to return to the traditional ways of the hacker, and a lament for the (potentially) waning power of an elite fraternity of programmers. I know I’m tiptoeing around the worst argument in the world . It isn’t a refutation of anti-LLM arguments to say that they are structurally similar in some ways to fascist arguments, any more than it’s a devastating critique to say the same thing about Lord of the Rings. Sometimes it is good to try and halt the march of progress! Some of our past traditions really were purer and more spiritually robust! It just bothers me, that’s all. I used to read The Story of Mel with unalloyed pleasure. Now it makes me nervous. If you believe you’re fighting the embodiment of fascism , or for the idea of value itself , what tactics are off-limits? What positions might you eventually come to accept? It feels wrong to directly associate my caricature with any actual posts, but it also feels wrong to make a blanket assertion without examples. Just so you know what I’m talking about, here are some posts that have elements of this attitude. I like some of these posts and dislike others. Page 329 of my copy of Julius Evola’s Revolt Against the Modern World . Ezra Pound, Canto XLV. “Usura” should be read as “usury”, or today we could gloss it as “capitalism”: all Pound’s examples of great art were from the pre-capitalist patronage era of art. Adolf Hitler, from his speech at the 1933 Party Congress in Nuremberg. Of course, there’s also historically been a strong pro -technology current in fascist thinking (even specificially Italian fascist thinking ). Page 134 of Blood in the Machine has a brief argument that Luddism was feminist because the (exclusively male) artisans’ wives would provide food for their meetings. No, really. From Kevin Binfield’s Writings of the Luddites , page 40. I’ve taken the liberty of re-rendering it in modern spelling and grammar. Aside from being too early, they didn’t have any connection to the state apparatus of power (in fact, they were ultimately crushed by it) and they famously lacked a singular leader. The example cited was BlindRSS . It feels wrong to directly associate my caricature with any actual posts, but it also feels wrong to make a blanket assertion without examples. Just so you know what I’m talking about, here are some posts that have elements of this attitude. I like some of these posts and dislike others. ↩ Page 329 of my copy of Julius Evola’s Revolt Against the Modern World . ↩ Ezra Pound, Canto XLV. “Usura” should be read as “usury”, or today we could gloss it as “capitalism”: all Pound’s examples of great art were from the pre-capitalist patronage era of art. ↩ Adolf Hitler, from his speech at the 1933 Party Congress in Nuremberg. ↩ Of course, there’s also historically been a strong pro -technology current in fascist thinking (even specificially Italian fascist thinking ). ↩ Page 134 of Blood in the Machine has a brief argument that Luddism was feminist because the (exclusively male) artisans’ wives would provide food for their meetings. No, really. ↩ From Kevin Binfield’s Writings of the Luddites , page 40. I’ve taken the liberty of re-rendering it in modern spelling and grammar. ↩ Aside from being too early, they didn’t have any connection to the state apparatus of power (in fact, they were ultimately crushed by it) and they famously lacked a singular leader. ↩ The example cited was BlindRSS . ↩

0 views
neilzone 1 months ago

Why are there no good tablets at the moment?

A friend was looking for a new tablet, and they asked me for a recommendation. And… I just don’t have one. The only good tablet, because Android can be replaced with GrapheneOS , was the Google Pixel Tablet, and that is no longer available. Secondhand prices are sky high. That was my go-to recommendation for a while. But it looks like Google has abandoned this project too. Amazon’s range of FireOS tablets are, IMHO, bloated with crapware which one cannot easily remove. Even the Fire-Tools scripts only get one so far. I can’t recommend one. There are some fun-looking “tablet computers”, but they are all expensive. A secondhand Surface Go, if one wants a Linux-based tablet, is readily available and pretty cheap, but honestly not what most people will want. And, while I like it as a cheap, touchscreen, Linux machine, it is not particularly powerful, which can be frustrating. And getting the camera working is a nuisance. I guess that there are some iPads, if one is accepting of Apple / iOS. Again, that wouldn’t be my choice, but I can see why some people like them. Why is there no good (non-Apple) tablet at the moment?

0 views