Posts in Go (20 found)
Anton Zhiyanov 2 days ago

Relying on Go

Everyone is creating a new programming language these days, often one that's "like Go but with more features" or "like Rust but simpler". Solod , a systems language for C and Go developers, might look like one of those languages, but it takes a different approach. Solod is not "Go-like" in the usual sense, nor is it an attempt to "fix Go's mistakes". At the language level, Solod is literally a subset of Go. Solod reuses much of Go's existing tooling, including syntax highlighting, LSP, linters, and the package management system. Take this quick-start guide, for example: Quick start Install the So command line tool: Create a new Go project and add the Solod dependency to use the So standard library: Write regular Go code, but use Solod packages instead of the standard Go packages: Run without saving the binary: There's nothing new here. It's mostly standard Go workflow, except for , which is a Go program that mimics . Solod also reuses a lot of Go's standard library code and tests. Some of it is taken verbatim from Go's source code, like these two string functions: Of course, Solod retains the Go authors' copyright. Some code requires changes to support the manual memory management with explicit allocators used by Solod: You can probably see the resemblance. Go tools don't know that Solod is a subset of the full Go language, so they won't flag features Solod doesn't support, like function literals or iterators. These diagnostics come from the custom tooling: Also, although a substantial part of Go's standard library is ported verbatim or with minimal changes from the original source, that doesn't mean the code is automatically correct. Solod still needs its own tests, including ones that run under sanitizers and static analyzers. All Solod code is translated to regular C11 and then compiled with GCC or Clang. Solod therefore relies on C tooling and decades of optimization work just as much as on Go's. Solod code: Translated C code: The C version is noisier, of course, especially for more complex programs than this one. But it remains readable. And since there's no runtime, interoperability between Solod and C costs nothing. A new language doesn't necessarily need a new ecosystem. Solod relies heavily on Go, and I see that as a strength, not a weakness. Reusing Go's proven tools and standard library makes Solod more reliable and easier to work with. If you're interested, take a look at Solod's readme — it has everything you need to get started. Or try it online without installing anything.

0 views
Chris Coyier 5 days ago

Outburst

Just had a little trip up to my favorite city of Juneau, Alaska. So lovely. Here’s just one story from the trip. There is a Big Thing going on there right now which is essentially an impending flood that is going to happen any day now. It’s an “Outburst Flood” or GLOF (“glacial lake outburst flood”). The weather.gov page explains in one graphic: So there is the famous Mendenhall glacier. Mendenhall lake, formed by it’s melt, is like 30 minutes from downtown Juneau, so it’s quite accessible for even cruise ship visitors to go get a look. Suicide Basin fills up with water during the warm months from glacial melt, and the Mendenhall glacier acts as an ice wall between it and Mendenhall lake. But at some point, the basin gets so full, the water from it starts flowing underneath/through the glacier into Mendenhall lake. There is a river from Mendenhall lake out to the ocean. When Mendenhall lake starts rising from the basin runoff, it goes out that river, and that river rages for a good couple of days. Hence the flooding. That river? Half the people in Juneau live in the Mendenhall Valley where that river runs directly through. Now it’s not all under evacuation flood watch, but some of it certainly is. And it’s rather unknown how bad any given year is going to be. It’s been very bad: My friend Justin lives in the Mendenhall Valley, not far from the river, and when it goes, he can loudly hear the river raging. As I type, we’re just days away from the prediction of August 8-12 from the basin draining. Justin and I climbed up Thunder Mountain (oof, it was actually 8.5 miles and 3,800 ft) and from the summit had a great look down at the entire valley, seeing the whole river area at once. Driving around there on roads by the river, you can see how the city has put up huge wall embankment things to hopefully stop the worst of the damage: Seems like it’s fairly unknown if it’s really going to work. If you’re interested, KTOO did a mini podcast on it last year getting into some details. Like one of the proposed solutions are literally bombing the glacier 🤔. Apparently now it’s leaning more toward the Army Corps of Engineers digging a drainage hole of sorts, but I don’t think any of it is totally sorted out yet.

0 views
Xe Iaso 6 days ago

SigV4 authentication is surprisingly complicated

SigV4 looks simple: sign a request, check the signature. Then you implement canonicalization, clock skew, and a cache that isn't allowed to hold your key. Tigris is a drop-in replacement for AWS S3 (or GCS, anything S3API compatible). As such, we need to be fully compatible with both the mechanisms and semantics of S3 including the SigV4 authentication protocol . This is the lingua franca of authentication in the object storage landscape; even Google Cloud Storage has a way to enable SigV4 support so you can use existing applications against its object storage service. At first I thought that SigV4 was fairly simple. Clients sign requests, servers do the same work and make sure the result matches. The main sticking point is that the cryptography involved is symmetric cryptography, the kind where both parties need to have the same secrets. This makes some scaling issues weird, but we'll get into that in the future. Note This is only going to be talking about authentication (ensuring the identity of a remote client), not authorization (ensuring the client has the permission to do something). Authorization will come in the future for reasons that will become obvious when you see that post. We basically needed to implement a compiler. That is not a typo. At a high level when a client signs a request with SigV4 you get an access key ID and secret access key. The access key ID is functionally a username and the secret access key is functionally a password. Admins can identify keypairs by the access key ID (without special training or tools) and services use the owner of the access key or policies delegated to that access key to determine what actions that client may take. SigV4 uses HMAC (hash-based Message Authentication Code) and SHA-256 (SHA-2 with a 256 bit hash width) to do authentication by creating salted hashes based on request metadata. In order to send a SigV4 request, clients take the outgoing request, reduce it to a canonicalized form, and sign it with a symmetric key derived from the secret access key, the current date, region of the service, and service name, kinda like this Go code: As an example, let's see what a signed request to a HTTP debugging endpoint looks like on the wire with and without the signature: And when you add the signature with : Note This is not a live keypair, it was specifically crafted for this post. Breaking it down we have two extra headers in the request: On the wire, HTTP/1.1 requests look kinda like this: However the headers could be sent in any order, and changing the order of request headers doesn't result in different requests. Additionally any query string parameters could be formatted in any way a client (or server) could imagine, including the use of semicolons to separate values . All attempts to canonicalise HTTP requests MUST deal with this ambiguity and define their own rules. SigV4 canonical requests are made up of a few parts: For that example request, the canonical form would look like this: As the request has no body, the empty sha256 checksum is put as the body checksum. Note This exact approach requires clients and services to buffer the entire request body before processing it. There is a subset of SigV4 that supports arbitrary-sized bodies without having to buffer the entire request using , which requires extra logic that is way out of scope for now. If you want to learn more, give your favourite AI agent the following prompt: Additionally, when you are doing presigned URL uploads in object storage, you replace the body hash with the fixed string when canonicalizing because you have no way of knowing what data the client will upload or what the SHA256 checksum will be. To make the signature, you take the sha256 checksum of the canonical request and then HMAC it against that derived signing key: And construct the header based on your access key ID, service region, and service name: AWS has made an extension to SigV4 that uses asymmetric cryptography called SigV4a (the "a" means asymmetric). Instead of using symmetric cryptography on both the client and server in ways that means the server needs to either know the client's secret access key (or a value derived from the secret access key), SigV4a uses key derivation functions to derive a cryptographic keypair. Servers authenticating requests fetch the public key from IAM. Only the client and IAM know what the private key is, and that private key is what signs outgoing requests. I'd love to use SigV4a more because it makes adding additional services to the mix (such as a git service ) a lot safer as you can have those additional services exist in different trust domains than the core product. This is the core of how microservices end up happening. However, it's not super widely used even within AWS. The only SigV4a use I can find in Amazon is S3 Express Zones , however they may end up using it in other services I'm just not aware of. When I did my own experimentation with SigV4a (where I was implementing my own IAM server so that I really understood this all at a low level), I had to copy a lot of internal AWS SDK code into my repo in order to get it working. I'll talk about SigV4a some more another time. One of the weaknesses of using signatures for API authentication like this is the problem of replay attacks. When you make a naïve signature of a value, there's no real way to tell when that signature was created. If you sign a request to create a compute instance at time instance t0, it's still technically valid at any other time instance tN. This is why the canonical form of SigV4 requests includes the current date and time: This means that the request was signed on July 15, 2026 at 20:54:32 UTC. Time changes constantly (at least at the rate of one second per second!) and the client has to have a working clock in order for TLS to work. Servers can trivially read the contents of and reject old requests. This means that you don't need to add or store nonce (number used once) values with each request because that doesn't scale . Note A lot of the security of this authentication protocol is predicated on TLS being used to encrypt the authentication headers over the wire. If TLS is not in use or is compromised by administrative policy, you're probably in a very weird exceptional situation that is very wrong in the first place. An easy example is an enterprise network with endpoint manglement software that does deep inspection of every user action. As a side effect of this, you need to set a temporal skew window for validating requests. This window needs to be generous enough to accommodate slow clients, sloppy timekeeping on the client side, highly latent clients, leap seconds , or other exceptional temporal phenomena. In general time synchronization is a surprisingly hard problem , so it's best to just be tolerant of clients in order to make things more robust in practice. AWS uses a temporal skew window of 15 minutes for validating requests. I'm going to use a window of 5 minutes for my API because 300 seconds is a nice round number and I don't have to deal with the same amount of legacy code that AWS does. So all of this SigV4 business had been working really well for Tigris. Then we worked with a few customers who needed a local cache to fully saturate their hungry GPUs. To be fair, Tigris is plenty fast, but the real thing that kills AI training is latency and something that runs locally will always be faster than the cloud. In order to provide that sweet middle spot between making everything rely on the cloud and having everything local, we made TAG , the Tigris Acceleration Gateway. This effectively gives you most of a Tigris region in your own infrastructure. When you connect to TAG, your code uses its existing access keypairs, buckets, and code. You point your code to TAG, you point TAG to Tigris, and then everything is cached for you. But how does TAG authenticate with your code? TAG doesn't have access to all your existing API keys (and to be honest it shouldn't), but it's still able to authenticate them with SigV4 authentication. TAG and the IAM server both implement a signing key proxying feature that lets a client and TAG both prove their identity to Tigris. Once that proof is sent, then TAG gets the intermediate derived signing key and uses that for locally validating requests, kinda like this: The actual implementation in TAG involves some derived AES logic so that the derived signing keys are very much limited to the client that requested it (namely: the AES key is the SHA256 encoded form of the proxy secret access key). One of the weird parts is that the canonical form of the proxied requests differ from the normal SigV4 canonicalization process, namely looking like this: This is signed using the same SigV4 signature process as before but added differently to the request: And then TAG reads the response from Tigris, caches those derived signing keys, and then uses those in the standard SigV4 process to authenticate clients: no round trip to the cloud required. The happy path is exactly what I thought it was. Reduce a request to a canonical form, run four HMACs, compare the result. That part fits in an afternoon. Everything expensive lives in the questions around it. Which bytes count as the request? Whose clock decides that a signature is still good? Who gets to hold the key that proves any of it? Each question has an obvious answer, and each obvious answer is wrong in some specific way you only find by implementing it. That last question is the one that surprised me. I read symmetric cryptography as a hard limit: if the verifier needs your secret, the verifier has to be Tigris. It isn't. SigV4 derives its signing key through a chain of four HMACs, each one scoped tighter than the last: date, then region, then service. Those intermediate values can travel without the secret behind them. TAG rides that. The key it holds stops working when the UTC date rolls over. It covers one region and one service. You can't walk it backwards into a secret access key. We also didn't write any of this, which is its own kind of relief. SigV4 is old, widely deployed, and hammered on by every S3 client in existence. Any compatibility bugs here are ours. The protocol's bugs are everyone's. The place a protocol bends is usually some intermediate value that somebody already designed to be thrown away. If you want a Tigris region in your own datacentre, the Tigris Acceleration Gateway caches your buckets locally and authenticates your existing keypairs with the same SigV4 dance your SDK already speaks. : The fixed string to signal to the server which authentication mechanism is in use. The rest of the string is information about the request signature so the server can properly canonicalize the request. : The date and time (UTC) of the request so the server knows when the request was signed. Servers will use this request date in order to reject old requests to prevent replay attacks . The HTTP method ( , , , , etc.) The URI path of the request ( , etc.) The sorted canonical query string (you must exactly match the server-side canonicalization logic) The signed headers terminated with two newlines The sorted list of signed headers joined by semicolons The SHA256 checksum of the request body : the HTTP Host of client requests (EG: tag.default.svc.cluster.local) : the Tigris keypair used to authenticate TAG itself (must be in the same organization as the client) : the time of the request in unix timestamp format : the hex output of signing the canonical form of the request against TAG's secret access key

0 views
Anton Zhiyanov 1 weeks ago

Going Backward

Go's standard library has a package with a function called . It lets you iterate over the elements of a slice in reverse order: If you're not deeply familiar with generics and iterators, the natural reaction to this signature (and to the others in the package) is: "couldn't this have been made simpler somehow?" To answer that, let's run a thought experiment. Let's picture ourselves as a distant ancestor, living in the pre-iterator era, who decided to implement from scratch. Our imaginary ancestor doesn't work at Google, so don't project their decisions onto the Go development team. They had their own reasons — and no Jira. A pleasant, sunny summer day, birds singing. You're at the keyboard as usual, and suddenly you decide to write a function for walking a slice in reverse order. Anything beats working on yet another Jira ticket. Usage example: The implementation is simple and works reliably. There's one drawback, though: creates a copy of the slice, which can be wasteful for large slices. Besides, the sun has hidden behind a cloud, and it looks like rain is coming. You decide to work a bit more. To avoid copying the slice, you decide to return a closure that knows the current position in the original slice and returns the next element on each call: Usage example: Now it allocates O(1) memory instead of O(n). That's better. Before moving on, you glance out of the window. Yep, sure enough, the rain has started, and the sky is even cloudier than before. Excellent working weather! Something about the calling code keeps bothering you. It came out rather imperative. You'd like to hand the loop mechanics over to and leave the caller with nothing but the application logic (whatever it is you do with the slice elements). You decide to complicate 's signature a little. Now it will return an iterator function that takes a callback as an argument and applies it to each element of the slice: The function returns a — that's so the callback can signal when it wants to stop the traversal early. Now you can turn the loop body in the calling code into a callback, and you don't need the loop anymore: Mmm, very functional. One small nuance: 's signature looks a bit heavy. You add a separate type for the return value: The function looks much better now: Praising yourself for inventing the iterator, you walk over to the window. It looks like the weather's gotten worse. The rain is coming down in buckets, and the sky is so overcast that it's grown as dark as evening. It's all great, but then it hits you: an ordinary over a slice returns both the index and the element's value. Your iterator returns only the value. You decide to fix this vexing oversight: Usage example: Since the result's signature has changed, it no longer fits the type. What can you do — you'll have to add a new type. After ten minutes of deliberation, you decide to call it : You get up to stretch your legs, and go to the window. The downpour is so heavy you can't make anything out. Lightning is flashing. Hail the size of your fist is falling — you've never seen anything like it in your life. Well, these things happen! Have you thought of everything? Seems so. But you're not going back to Jira tickets just yet. Refreshing your memory of the Go spec, you realize that besides ordinary slices there are "user-defined" ones — types whose underlying type is a slice: works perfectly well with — the compiler accepts a value of type since its underlying type is : But what about this? Here's where the difference between and shows up. When you assign the function itself, it's the signatures that get compared: versus . Signatures match only if the parameter types are identical. But and are different, even though one is based on the other. The signatures differ → you get an error. Scratching your head, you turn to the spec once again and find a special generic syntax: . It represents the set of all types whose underlying type is . Just what you need! Now you'll have to parameterize not only the element type ( ) but the slice type ( ) as well. is needed for the returned values, while lets the function accept not just , but any types based on it: Now the example: It works! You've ended up with something similar to from the package. You exhale wearily and walk over to the window. The downpour and hail have given way to a hurricane. Trees and billboards go flying past. Toads, for some reason, are falling from the sky. To take your mind off the strange events outside the window, you keep pondering. An ordinary is already great. But it would be even better if the traversal logic itself were configurable. On the other hand, if you end up with a lot of parameters, a strategy would suit better. And, by the way, it wouldn't hurt to add a factory that produces iterator factories according to given criteria... Before you can finish the thought, the ground outside the window tears open with a deafening roar. An enormous black hand, streaming molten lava and flickering flames, bursts out of the fissure, seizes you, and drags you straight down to hell. P.S. Despite the article's tongue-in-cheek tone, the "complicated" version in the standard library is justified ( just follows suite with other package functions). But if you're doing something similar in a project that solves a specific problem — it might make sense to stop at the simpler option.

0 views
Anton Zhiyanov 2 weeks ago

Solod 0.3: Concurrency, JSON, more safety

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

0 views
Unsung 2 weeks ago

“Creativity is fundamentally not an efficiency problem.”

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

0 views
Simon Willison 3 weeks ago

A Fireside Chat with Cat and Thariq from the Claude Code team

Earlier this month I hosted a fireside chat session at the AI Engineer World's Fair with Cat Wu and Thariq Shihipar from Anthropic's Claude Code team. We talked about Claude Code, Claude Tag, Fable, coding agent security, evals, tool design, and how Anthropic use these tools themselves. The full video of the session is now available on YouTube . Below is an edited copy of the transcript, with extra links and my own bolded highlights. A few top-level notes if you don't want to watch the video or wade through the whole transcript: Simon: Claude Code came out in February of last year — it's under a year and a half old, and it was originally just a bullet point on the Claude Sonnet 3.7 launch . How has what you do on a day-to-day basis changed in the past year , now that we have these coding agents that actually work for us? Cat: I remember when we first came out with Claude Code and Sonnet 3.7, you would give it a task and you would have to closely monitor every single little thing it tried to do. I would read every permission prompt extremely carefully. I would frequently say no — no, no, no, did you check this file? Did you check that file? And now it's been incredible with every model generation. I feel like we've all gotten a chance to take a step back and delegate a lot more of the menial implementation to Claude . It's freed up a lot of our time to think about more creative work, like: what is the right experience that we should be providing to our users, now that we know Claude Code can implement a lot of it? And now with Fable it's a totally different step change improvement. We see for a lot of our use cases that you can actually one-shot a ton of features with Fable now . Thariq: I remember the first text I got about Claude Code. One of my best friends was like, "You need to go try Claude Code." It was about when Opus 4 came out, and I tried it and I was like, "Oh, shit. I need to work at Anthropic now." And that was Opus 4 — great model, but you were reading permission prompts. It's kind of crazy how much amnesia we have, where I'm like, oh, auto mode has always been here, right? I don't even remember pressing yes and allow. For me, the big thing I'm trying to push myself on is that we have to do higher quality work than we've ever done before . The outputs are incredibly high quality. I've been using it to edit videos a bunch , and I'm like, okay, it has to meet the very exacting demands of our brand team in a couple of hours or we just can't do it. That's how I'm trying to shift with Fable: the best work we've ever done, faster than we've ever done it before . Simon: What's a piece of conventional software engineering that was true a year ago that you don't think holds anymore in this new world? Cat: One of the biggest shifts we're seeing in the eng skill set: two years ago it was pretty typical for a product manager to go talk to a bunch of customers, align over the course of six months with cross-functional teams on some PRD, and write a thorough spec on exactly how we'll implement this before the first line of code gets written. Now things are completely turned the opposite way. For a lot of engineers, the push I would give to folks in the room is to develop more of your business sense and product sense on what it is we should build , because the timeline between having an idea and building it is so much shorter — it's down from six to twelve months to maybe even a week. That means all of us need to have better taste on what is worth building, what will actually inflect the businesses we're working on. So it's an increase in value on product taste and business sense , and a bit lower on execution in most product domains. Of course, for infra there's still a very heavy emphasis on making sure all the details are right. Thariq: For me, it's that rewrites are now good . Simon: The worst thing you could do is now actually fine! Thariq: Exactly. All the Mythical Man-Month stuff — never rewrite — I'm pro-rewriting now. If you have a good test suite — and I think the rewrite actually forces you to make sure you have a good test suite — but I think what people undercount is that a codebase is a spec, and maybe it's the only copy of the spec that you have , because no one knows every branching part of the codebase. You can take this as an artifact and distill it or create other versions of it. We rewrote Bun in Rust and it works great — it's live for me right now. Simon: You're not shipping Claude Code on Bun-in-Rust yet, right? Thariq: Internally we have. (Actually it looks like Anthropic started shipping Claude Code on Bun-in-Rust to everyone on June 17th .) Simon: The other big launch recently was Claude Tag — that's what, a week old now, at least for the rest of us. I understand it's being used at Anthropic by non-engineers a great deal. What kind of things are non-engineers doing with Claude Tag? Cat: Claude Tag is a Claude that lives in your team's collaboration tools. We launched it last week within Slack. The thing that's different about Claude Tag is it's multiplayer by default . Once you add Claude Tag to a Slack channel, you can chime in, your teammates can chime in, and you can collaborate together on the PR. The other big difference is that it's proactive instead of reactive. You can tell Claude Tag, "Hey, monitor every bug report in this channel, put up a PR to fix it, and tag the engineer who last touched this part of the codebase," and it'll do it for the lifetime of the channel without you having to manually tag it in. And the third big shift is that we've added team memory into this . If you tell Claude Tag your preferences in the channel, it'll remember them for every future post. If you always want it to debug outages but you don't want it to debug warnings, just tell it that in natural language in the channel and it'll remember it for you and everyone else on your team. Internally, we see Claude Tag as the evolution of Claude Code. We see this as a large shift in how we work internally. Claude Tag currently lands 65% of our product eng PRs. Simon: For all of Anthropic, or just for Claude Code? Cat: This is just for our product engineering team — our internal version of Claude Tag lands 65% of our product PRs right now . And this is a huge shift; this is more than 50% of our PRs. The way we see people split work between Claude Code and Claude Tag is: Claude Code is still the best place for your most complex tasks, when you're interactively iterating with the agent. But Claude Tag is great for having it work proactively on your behalf , so you no longer need to manually kick off Claude Code for all the bug reports that come up for features you're working on. Thariq: And for non-coding cases: for example, before this talk we asked Claude Tag, "Hey, when is Fable releasing?" We wanted to make sure we'd line it up with the announcement. Claude Tag would search our Slack and look at who's been saying what. As a search engine for your company, it's really valuable. It has all the context for your product, so you can ask it metrics-related questions — often when you're making decisions you want them informed by what the metrics say, so you hook it up to your event store. I've seen our marketing team do things like, "Hey, tell me about this feature." They're not programmers, but Claude is a programmer — it can clone the codebase and say, "This is the feature, this is what it looks like, this is a recording of me using the feature ." It enables a whole wide variety of things, and I think we're still early in figuring that out. Simon: One of the problems I've had with coding agents is that I get how to use them as an individual, but I'm not really clear on how to use them in a team environment. It sounds like Claude Tag is your current answer to that team collaborative layer for this stuff. Cat: Exactly. And a large percentage of our sessions are actually multiplayer right now. Maybe I say, "Hey, I think we should implement this new feature in Cowork," and I'll tag in Claude Tag to do a first pass at it. Then I'll tell Claude Tag, "Share a recording of your final implementation," and I'll tag in design to take a look. They'll nudge it, then pass it on to eng to take it to the finish line and get it out to prod. It's been this very fluid experience. We're still trying to iron out what the social dynamics are for steering the same session , but we've found that people just observe how others use it and follow those social norms — it's been pretty intuitive for us to integrate Claude Tag into our teams. Thariq: It's great for teaching people, and also for reducing slop, because the fact that everyone is seeing you use Claude together sort of levels up how you use Claude as well . This reminded me of how Midjourney solved the challenge of teaching people advanced image prompting by enforcing prompting in public in their Discord channels. Something I've found really hard myself is knowing when a feature is worth shipping now that the cost of actually building features has dropped so much. Simon: How do you deal with the hardest problem in all of engineering — prioritization? How do you decide which features are worth building and shipping when building a feature is so much more inexpensive now? Cat: This is the hard thing. There are a few ways we approach it. One is we dogfood our products every single day. Whenever there's something we want to be able to do in our products that we're not able to, instead of finding a different solution we fix our product so it can support that case. We have a very heavy dogfooding culture internally. Before we share our products with everyone in the world, we share them with everyone within Anthropic, and with some early customers who give us very honest feedback about it — the more brutal the better — and we iterate until people love it. We have an internal bar for the number of active users and the amount of retention a feature has to have before we share it with the world. Because this bar is very clear, every engineer knows what they're trying to hit. I think this also levels up our polish, because if the feature isn't polished, people will churn — and then we shouldn't ship that feature. Using internal user-retention to decide if a feature should ship makes a whole lot of sense to me. Simon: Do you have an example of a feature which surprised you? You rolled it out and the engagement was off the charts — something unlikely to be shipped that turned into a real product thing. Cat: I do have one. A lot of folks on our team love remote control . Remote control lets you use your mobile device, or Claude in the web browser, to connect to a local Claude Code session running in your CLI. I never have this need, because I just kick off the task directly on mobile and it runs in a cloud session without using my local environment — I think because I'm doing very easy coding tasks. It was something I didn't totally understand; I was like, hey, people should just set up remote dev environments. But in practice, once we rolled out remote control, so many people I talk to told me that what they do every night is plug their laptop into a power charger, open a bunch of remote control sessions, lock the screen, and then use their mobile phone from their couch to control Claude Code . So this has become a flow we're now leaning into that I didn't originally get — but now I do. One of the over-arching themes of the conference was review: how much attention to people spend to reviewing code written for them by coding agents. I was very keen to hear the Claude Code team's take on this! Simon: How does code review work? Does a human being review every line of production code that makes it into Claude Code? And if not, what are you doing — how do you keep the quality up? Thariq: It varies on the task a lot. For important areas we have code owners. The system prompt is an example where we have a code owner — you really need to get their approval. Simon: So the code owner is directly responsible for the quality of that area of the code. Thariq: That's right. Cat: And they need to approve any PR that touches it. Thariq: We have our code review GitHub bot review everything — that goes on every PR, and often it's doing the bulk of the review. Something I've seen on the team is that for more complex PRs you might make an artifact to explain the PR so that other people can then review. And we invest a lot into verification, CI/CD, things like that, to make sure that any time anything fails we have a test. We have a really robust environment where Claude can control Claude Code and test it. So there's a multi-pronged approach to code review. Cat: In general, we are trying to move to a world where humans don't need to be in the loop . For the most critical changes to the core of Claude Code, and the cores of other products, there is always a code owner and they do manually review all the changes. But increasingly, for the changes at the outer layers, we actually have Claude code review fully review those . That sounds pretty scary, but we've had a six-plus-month-long process to get here, and there are baby steps that you take to build up trust with code review . In the beginning we had human review for everything, and then increasingly we would say, okay, for code changes that touch these files, code review is catching 100% of the issues there — so we actually don't need a human manually reviewing those . And when we have incident review, we look at the PRs that caused the incident and say, okay, how do we update code review to catch that? — and we take those PRs and add them to an eval set to make sure our future changes to code review never regress that metric. Removing humans from the code review loop is a big step forward. It can sound scary, and it's not something you can do overnight, but it is something you can do through many months of investment in the infrastructure to give you the confidence that code review is catching everything you care about. So the key seems to be constantly iterating on the automated review systems themselves, in order to build trust in them over time. We got deep into evals - another hot topic throughout the wider conference. Simon: I know that Opus 4.8, if I ask it to build me a JSON endpoint that runs a SQL query and outputs JSON, is just going to get it right — that's not something I have to review closely. But then a new model comes along and I don't know how to build trust in Fable quickly, that it's not going to mess things up that Opus didn't. How does the new model affect your intuition for what it can do and what it can't do? Cat: The main reason we're building up this eval base over time is so that new models can be a drop-in replacement . When we have a new model, we run the whole eval set and make sure that, for example, Fable is strictly better than Opus 4.8 — and that gives us the confidence to drop it in. Simon: Are those model evals for Anthropic as a whole, or Claude Code team-specific? Cat: We have both. We have evals on our team, and we run code review across every repo within Anthropic, so we have evals for that. And for things like auto mode, we not only have evals across every user within Anthropic — we've also commissioned multiple external testers to red team it, to create environments with prompt injections and malicious inputs, and make sure that auto mode doesn't let any of those pass . Simon: I want to know if the system prompt improvement I made actually improved the product — that's the most basic form of product-specific eval, and I still don't have a great feel for how to do that. Is that something you're doing such that you have complete confidence that a tweak you've made to the system prompt results in better output? Cat: We don't have complete confidence, but we do a lot to make sure that we don't regress performance. The starting point is a suite of external evals that we trust, and we complement that with an even larger suite of internal evals that we trust. To start, we mainly optimize for capability : given a complete definition of a task and the full codebase, does Claude make the right decisions, fully fix the bugs, and pass all the tests? That's the starting point and the thing we optimize for, because it's most directly what users want. But there are a lot of behaviors that impact how users feel when they work with Claude Code. For example, people really don't like it when Claude Code says it's time to go to sleep. Or people really don't like it when it says, "Hey, I finished two out of five parts — do you want me to continue?" Yes, please continue. So we're building up a set of behavioral evals to catch these. And as we get user feedback — please be loud with us about your user feedback — we rank the priority issues and go down one by one and build evals for each of them. It's not 100% coverage, but it is a priority for us to increase the coverage. Simon: How much interaction is there between the Claude Code team and the teams at Anthropic who are training the models in the first place? Is that quite a close collaboration? Cat: Across Anthropic, we all work quite closely together. We meet often to talk about what we expect the next generation of models to be able to do. Our research team has also been amazing about showing this publicly — we often talk in our blog posts about how we're targeting ever-increasing longer-horizon work , and how we train Claude itself to be honest, harmless, and helpful. We also put a lot of effort into making sure it's aligned with your intent, even if your intent is expressed in a fuzzy way. Of course, try your best to be specific about what you want, so Claude has all the context — but even when you're not specific, we teach Claude to make good assumptions. It's been a productive partnership. So many useful prompting tips in this section! Simon: Thariq, you mentioned this morning that the system prompt for Claude Code has been reduced by 80% because of Claude Fable . Can you go into a little more detail? What kind of things have you been able to drop? Thariq: It wasn't just Fable — it was Opus 4.8 as well, and going forward, future models. We have different system prompts for different models now. One of the patterns we saw is that we were over-constraining Claude. The initial, maybe Opus 4-ish models wanted a lot of examples, and removing examples was extremely helpful , because it was just more creative than the examples we gave it. Simon: That's really interesting, because one of the top prompting tips I give people is: give it examples. If that's no longer true, that kind of breaks my prompting model a little bit. Thariq: Same here — I was surprised to hear that. I think now it's more about the shape of what you give it — the tools you give to Claude, your system prompt, things like that. The other thing we did is try to give it more context and fewer "do not do this" instructions, because that's a very strong impulse for Claude, and especially if it conflicts with user instructions later on, that can be extremely confusing to Claude — "I've got this skill that says this and the system prompt says this." So we try to have fewer hard constraints, more context, and fewer instructions overall . It's definitely a science — it took a bunch of evals to build. Cat: In general, when you're prompting these models, you should always think: are there edge cases to the instruction that I'm giving it? When we went back and reviewed all the instructions in the Claude Code system prompt, we found a few cases where yes, this statement is 90% true, but there's a real 10% of cases where it's not true . We didn't want to constrain the model, or confuse it into thinking it should always do this. One good example is verification. Everyone here wants Claude to verify its work, and we had some instructions in the prompt that said: if you make a front-end change, always verify. But there's a limit to it. If it's changing copy from one string to another string, and the user says "just make a quick fix and update the test," maybe you don't want to verify. So we've adjusted our wording from "always verify, verify, verify" to something like: most of the time when you're doing front-end work you can't fully understand the experience by hitting the backend endpoints, so when you make larger changes to the user experience, please run the app locally. And in fact, that instruction probably isn't even good either, because what is a large change? Maybe it should test small changes too. In general, whenever you give a prompt to the model, you should think about the ways in which it could be misinterpreted by a well-intentioned human , in order to better understand how the model might interpret it — and soften the prompt so that it's actually 100% accurate, because you're giving this prompt to the model 100% of the time. Simon: What's fascinating about that is you're relying on the model's judgment — and that's got to be an Opus/Fable-level thing. Models a year ago did not have the level of judgment necessary to decide whether they were going to test a change or not. But that does break down if you're building for a wide range of models and trying to run the cheaper models for cheaper tasks. Cat: We actually have a different system prompt per model now , for this very reason. It's only our most frontier models that have this 80% token decrease — the older models still have the full system prompt. Simon: Do you think Fable and Opus are smart enough to prompt Haiku with more details, because they understand that Haiku has less judgment, less taste? Cat: We haven't been able to eval it — we don't have any hard data to show it. Thariq: There's a tough thing with smaller models sometimes, because sometimes the larger models can be more token-efficient on a hard problem than the smaller models . So there's a bit of intuition to build there — sometimes you really just want frontier intelligence almost all the time. The Pareto curve shifts, and it's hard to find. Simon: A year ago I did not trust a model to write a prompt. Today the good models are very good at prompting — a lot of my prompts are written by models, which feels absurd but works really well. What helped me come to terms with that was thinking about subagents, which are entirely about a Claude model setting up a prompt for another Claude model. Thariq: Workflows are actually a really good example of this, because it's Claude not just prompting a single subagent, but prompting the orchestration of many subagents, and each one of them gets a very detailed prompt. It's almost a level above just spawning a subagent. I've also been using it on my personal machine, giving it the Gemini API and saying: here, generate images . It's way less lazy than I am at prompting an image model. It's just Claude prompting Claude all the way down. Cat: I think Claude also wrote the prompt for the workflow tool . Simon: I've read that prompt — it's a good prompt. That's actually a frustration I have with Anthropic generally: you publish the prompts for Claude Chat , but you don't include the tool prompts and the Claude Code prompts. I still have to run a proxy to intercept them. I would love it if the Claude Code prompts were deliberately published — they're the documentation. They're how you know what the tool can do and how it works. Cat: I'll write down that feature request. I'll have Claude Tag do it. Interesting to note that OpenAI's prompting best practices for GPT-5.6 includes similar advice for their latest models: Favor leaner prompts Removing repeated instructions and examples and simplifying tool descriptions can improve task performance and token efficiency. In a sample of internal coding-agent eval runs, configurations with leaner system prompts improved evaluation scores by roughly 10–15% while reducing total tokens by 41–66% and cost by 33–67%. Simon: Claude Code is basically a big bag of tools. What's your bar for introducing a new tool? How do you decide when it's worth doing that additional engineering at that level? Cat: Do you want to take it? You introduced one of the best tools we have. Thariq: My career peaked when I introduced the ask user question tool. It's really hard. Especially for some tools — ask user question is Claude's tool to ask you — so it's hard to eval, and sometimes it's more of a user preference thing. Back then we had fewer evals, so it was very dogfooding based — or "ant fooding," our ant version of that. But overall we've been trying to trend towards fewer tools . The last set of tools we introduced was the task tool, I think — and we try to give Claude more general versions to do things. I have a long-running fascination with file editing tools - they were the subject of the old Aider code editing leaderboard , and I've watched with interest as they've evolved in different coding agents from search-and-replace based to line-number-based to more complicated patterns. The Claude API docs describe a text editing tool that's recommended for building against the API, but Claude Code seems to use slightly different approaches here. Simon: One of the most interesting tools is the file editing tool — you can have file editing as a tool, or you can tell it to use sed and grep and do things that way. What's the latest evolution of your file editing tool? Thariq: We still have one, but for example we removed our grep and other search tools — glob tools — in favor of native bash. Like I said in my talk earlier, the models are kind of more of a biology than a physics , and tool design especially is quite hard. I'm not sure if Cat disagrees and thinks there's a science to the eval of it, but I think tool design is more of an art, maybe — or a biology. Cat: I largely agree, but in general as we introduce more tools, we try to keep the cardinality pretty low and make sure that every tool we add has a distinct function from every other tool, so that Claude can very easily distinguish when to call each . For file edit, the reason we have it is actually because we can render it. We show people when Claude makes a file change, and there's this nice dedicated UI that says: do you approve this edit to this file? The reason we had a dedicated file edit tool was so that we could deterministically know that Claude was making a file change, so we could show people this nice UI. A lot of new users onboarding still really like this experience, so we've kept it around. But for a lot of us who are on auto mode right now — hopefully you're not on YOLO mode — I don't think it actually matters, and we could probably just remove file edit and be totally fine. It's the prompt injection question! Who better than Anthropic employees to explain how Anthropic sees the risk of prompt injection attacks causing their Claude Code instances to run amok? It turns out they really trust their auto mode - and see that as the feature that enabled Claude Tag. Simon: Let's talk about safety and security. I am deeply aware of the risks of prompt injection, and there are so many bad things that can happen if somebody else tells my Claude Code what to do. I still mostly run Claude Code in YOLO mode and feel incredibly guilty about it. What's the advice within Anthropic for safely running Claude Code? Cat: Why not auto mode? Simon: I am starting to use auto mode, but I don't understand it enough to get how safe it is. As of maybe three weeks ago, I'm defaulting to auto mode. Cat: Broadly within Anthropic, almost every single person uses auto mode. It is the best way to do long-running work in Claude Code while being safe. We've done extensive bashing. We have thousands of evals. We've commissioned many red teamers to create adversarial environments in order to trick Claude Code into doing bad actions, and we've mitigated every single issue that they found. We're going to publish some evals in the coming weeks, but we've pretty much mitigated every attack. Simon: That is a big claim. Cat: We'll share the evals for it so folks can assess, but we've been extremely diligent about identifying all the ways in which Claude might mess up and then updating auto mode to counter it. It doesn't catch 100% of things — that would be way too strong a claim. But for the main categories of risks that we're concerned about, like prompt injection and data exfiltration, the risks are far lower than the average human reviewer . I am very much looking forward to learning more about their evals and approach to verifying auto mode. Thariq: A little on how auto mode works — it's useful to build this mental model. Whenever Claude is doing a turn, or a bash call, there's a Sonnet classifier that is judging the tool call and also the context of the conversation — your instruction. There are some things around permissions that are dependent on your request: you don't want to give git push permissions all the time, but if you say "push this to GitHub," you want it to do it — and if you say "don't push," you want it to deny it. Auto mode will do that. That particular thing happens to me a lot, where Claude tried to do something because it's very helpful and proactive, and auto mode saw "don't do this" and surfaced it. So it's good at the dynamic permissions that you yourself give inside the prompt, which I think is really important. It also works well with our sandboxing infrastructure , because sandboxing is one of those things where there are so many different edge cases that it's hard for us to deterministically follow them. We have a sandbox, and when something needs to escape the sandbox — like a network request — auto mode can look at that request and ask: does this make sense? — and allow it. Simon: I hadn't realized auto mode is interacting with the networking sandbox as well. Cat: It interacts with any permission prompt the user would otherwise see. Simon: How old is auto mode? As a feature I had access to, it's only a couple of months old, right? (It was first made available to the public on March 24th .) Cat: We've been using it within Anthropic since January , so we've been hardening it for quite a while. Anthropic is extremely focused on safety and security, and we've been working broadly across our alignment and safeguards teams to enable the rollout internally, build out these evals, and make auto mode even more robust before sharing it with the world. Thariq: This is also the reason Claude Tag is so good — Claude Tag uses auto mode . I've heard a lot of build-versus-buy questions about a Slackbot, and I'm like: please, you probably shouldn't build your own AI Slackbot. There are so many attack vectors. You have a feedback channel that users can post feedback into, and now your bot is reading it. The work we've put in with auto mode — and we have a general Swiss cheese defense for security; we also RL against this stuff — I think this is really what makes Claude Tag work . It works seamlessly with your permissions, and you don't want to be prompt injected in your Slack. Simon: Are there any more security things in the pipeline that go beyond auto mode? Thariq: I think we're very secure. With Claude Tag you can provision your own credentials for Claude , so it doesn't need to act on your behalf — you can have Claude as an identity, and that also makes it easier to audit and inspect what Claude is doing. Simon: Because Claude Tag is influenced by anyone who can talk to it — it's got a much wider pool of people telling it what to do. Thariq: That's right. And of course we have probes as well with Fable, which is a downstream effect of our safety and research work. I think this is the moment where you see Anthropic being an AI safety company really paying off: we really want Claude to be able to run in an aligned way over long periods of time , and auto mode has to be basically flawless for this to work — it's all downstream of our being an AI safety company. Cat: We also launched trusted devices for the remote control users out there who want to be safer. And for all of our remote environments, we support credential injection . If you want Claude Code to be able to access Datadog, but you don't want Claude Code itself to hold the Datadog credential, you can set up our identity and credential management system so that the Datadog credentials are only usable by the agent but not accessible by the agent — we insert them on the fly when the agent tries to make a Datadog request. I really like that credential injection pattern, where Claude Code can access an API via a proxy and that proxy both audits the request and injects the relevant API key - so Claude can access authenticated endpoints without having access to the API credentials itself. Thariq talked about a sense of grief brought on by Fable-class models in his keynote in the morning, and we dived further into that as part of our conversation. I've been calling this Deep Blue . Simon: Let's talk a little bit about the human element. A lot of people are feeling a sense of loss now that so much of what they considered to be their role in building software is being subsumed by the models. How do you think about that? How has the past year and a half changed the way you think about your own craft and the value that you add? Thariq: Cat and Boris are such good reminders that you have to be more ambitious. They're always like: we're growing so fast, we have to be on the edge, we have to do the best work we can. That's a constant reminder for me — any time I'm slow on something, I'm like, okay, can I do it faster? Can I be more ambitious here? And oftentimes the answer is Claude, because Claude is getting better as you go — the last time I tried this, it was with the previous model. On your point about loss: I think this is real. If you're only trying to do the same work you were doing before LLMs, and now it's a prompt, it is, I think, kind of a sad feeling. And the way you offset that is by being more ambitious. I think Jared is such a good example — he hand-wrote all of the Zig code in his Oakland apartment in about a year, barely left his house, and had so much fun doing that. Now I see him rewrite all of Bun into Rust and he's having so much fun doing that — it's so much more ambitious, and that's how he offsets it. Generally it's asking how do I do the bigger thing and do more — I think success is fun . It's changing your ambition. "The way you offset that is by being more ambitious" neatly captures where I've landed on this issue myself as well. Simon: And Cat, what does that look like from a product management perspective? Cat: I feel like the product role just changes every single month. All the PMs on our team are this mix of engineer, designer, PM — most of them actually used to be full-time engineers. For us it really means plugging in whenever there's any kind of gap . If we have an idea and we didn't inspire any engineer to go build it, then we should just build it, put it into a notebook, and inspire people to take it to production. If the designs look a little off, let's take a page that's similar, do a first-pass design, and tag in someone who's very detail-oriented to fill in the gaps . Or if we notice that our team and product adoption is bigger within the company, and more people need to know what's coming down the pipe for Claude Code, Claude Tag, and Cowork — let's automate figuring out our whole launch calendar, let's automate getting those status updates asynchronously so we're not bugging people, and make sure our updates in our internal announce channels are fully detailed and to the point. For us it's very much understanding what the gap is right now between a great idea and getting something to our customers , and how do we automate it as much as possible . This reflects something I've noticed: when you can produce code so much faster, time spent blocked awaiting a decision from someone else becomes a much more notable bottleneck. Engineers who can make product decisions can move a whole lot faster, and the cost of getting one of those decisions wrong is much less prohibitive. Simon: What's a moment when Claude has surprised you? When the model did something you didn't think it would be able to do? Thariq: I've posted a lot about Claude video editing, but most recently I gave a talk at the ACM Agentic conference, and I asked, "Hey guys, do you have the edited video? I'd love to post it and share it with my comms team." They said, "Oh, it's taking so long." So I asked for the raw files. They sent me the video of me talking on stage, the video of the deck, and the audio file, and said, "Good luck." I gave this to Claude, along with my HTML deck, and said, " Hey, can you just edit this together? " And what it does is honestly incredible — I'm ready to ship it. It transcribes the entire video. It notices that sometimes the video of my deck is a little weird — there's a popup of an auto-update in the middle — and it goes, " Oh, I probably shouldn't use the video of your deck. What I'm going to do is slice it up, figure out which slide you're on, and use the HTML source instead. " So it displays the HTML source. Then it's got video of me, but I'm only taking up a small part of the stage, so it's cropping dynamically to where I am on the stage — and I'm pacing, so it's tracking me as I pace. And it's transcribing what I'm saying. Simon: This was Fable, right? Thariq: This was Fable, yeah. It was a good prompt, but it was a one-shot prompt. Then I asked it to add some interesting animations and graphics, and I was just blown away. It does ffmpeg, it does Remotion. Here's Thariq's video on how he used Fable to edit Fable's own launch video , and here's that launch video . I'm embarrased to admit that I've been finding it quite hard to come up with tasks that frontier models like Fable 5 and GPT-5.6 are unable to accomplish. Cat still doesn't rate its UX design skills: Simon: What can't it do? What are the things where you're still disappointed — where you're waiting for Claude Fable 6 to figure it out for you? Cat: I want it to have better design and UX taste. It's now at the point where if I write out a prompt with a detailed spec of how I want a feature to behave, it will usually behave that way. But the paddings might be off, or the interface just isn't delightful yet. It leans on existing best practices for how apps are designed, but for frontier AI products, there are so many new interaction experiences that we have yet to design . Simon: There's an Opus aesthetic — you can look at something and go, "Yeah, that was designed by Opus." It'd be good if we could move beyond that. Cat: Yeah. I'm very excited for future models to hopefully be interaction design thought partners . Thariq: What can't it do? I would love to see it interact more with the real world. Can it solve science? Can it orchestrate the experiments? There's some amount of coding that goes into that, but there's also this other taste of the broader world that it needs. I figured this would make a great closing question: Simon: Which parts of Anthropic's company culture do you think uniquely help Anthropic be productive with these tools, that other companies should steal? What are the cultural hacks people should be adopting from you? Cat: I'll share one for Claude Tag. Claude Tag works best when you have it in a public channel, and when most of your channels are public. Claude Tag is able to search across all public channels to get as much context as possible to give you the highest-accuracy answer — and it's only able to do this if it has access to everything . Thariq: I mentioned this in my keynote, but it's so important to me I want to re-emphasize it. The co-founders say we don't negotiate against ourselves , and I think this is really important. You can imagine trade-offs in your head and talk yourself out of doing something ambitious — or you can just try to do the ambitious thing. We're so often asking: what if we just did it? Is this a real trade-off or not? And if so, why — where's the proof that it's a real trade-off, and not just something that sounds reasonable? Make the trade-offs show themselves to you. Be as ambitious as you can. I couldn't resist throwing in this one as well. Simon: What's one of your favorite absurd things that you've built with Claude, just because you could build it? Thariq: I'm working on a 2D Street Fighter fighting game with me as a character — and my friends as well. It uses Claude Code to prompt Gemini — and honestly the Seedance model is pretty good — to make video animations. It works great; it's so good at prompting, and it can verify the frames to check whether an animation was good. Simon: Is this Street Fighter 2-level 2D sprites you're generating? Thariq: Yeah, exactly — 2D sprites. The animation looks amazing. And it can also figure out hitboxes — it can be like, "Oh, your fist is here, I'll draw the JSON hitbox." It's incredible. Cat: Mine is much more simple. I'm a big rock climber and a lot of my friends climb, so we have this little app we built with Claude Code where we log all the projects we're working on. We also go outdoors together a lot, so we have Claude do all this research with workflows. Workflows is amazing — we brand it as a coding tool, but it's amazing for doing deep research for travel. I also plan our team offsites, and it's good at finding venues that can fit all of us. I use workflows to research all the climbing destinations we might want to go to, and what has direct flights from where all of us are located. It goes to Mountain Project and finds all the climbs at our grade level. It finds the Airbnb. And I don't like hiking, so I care a lot about it having a very short approach — very short walking distance from where the car parks to where the rock actually is — and it filters for this. With existing apps I have to manually click through Mountain Project, but with this I just put in all of our preferences and it's a custom app for us. Simon: So you're basically vibe coding Jira for mountain climbing. Cat: Exactly. We had a few minutes at the end for questions from the audience. Audience: Do you have any near-term plans to build more eval tools for us to build eval datasets, and more observability tools to monitor the performance of agents and workflows? Cat: We've considered building eval tools, but I think the limiting factor actually tends to be that it takes a long time for customers to build really high-quality evals . So I think the tooling is less of the constraint, and more the skill set of how you build a great eval. That's an area where we're excited to both invest internally and hopefully share some best practices externally. Audience (Sai): I'm interested in the memory and the multiplayer. How is memory being designed today? I assume it's around files. And second, have you thought about an orthogonal direction where you would actually need a data store for these memories, instead of files, to scale it better? Thariq: Right now for Claude Tag the memory is channel-specific. Every Claude in that channel has a shared memory, and the instances have a session — but the session can contribute back to main memory. We do a lot of memory research, and it can be kind of unintuitive what the right way to do memory is. We're always running memory experiments. How it works right now in Claude Tag is a markdown file per channel. You are only seeing the long-form articles from my blog. Subscribe to /atom/everything/ to get all of my posts, or take a look at my other subscription options . Claude Tag (Claude's new collaborative Slack integration) now lands 65% of the product engineering PRs for the Claude Code team. Claude Code ships features to Anthropic employees first, and only ships the features that demonstrate user retention with that cohort Critical changes to Claude Code are still reviewed manually, but the team increasingly relies on automated code review for the "outer layers" of the product. Adding examples to a system prompt is no longer best practice for models like Fable 5 or even Opus 4.8. The Claude Code system prompt recently reduced in size by 80% . Likewise, lists of " don't do X and don't do Y " can reduce the quality of results from the latest models. Dogfooding inside Anthropic is called " ant fooding ". Anthropic really believe in their auto mode , and see that as an enabling technology for Claude Tag. Thariq advises offsetting coding-agent-induced Deep Blue by " being more ambitious " with the work you take on. Fable is competent at editing video , and Thariq used it to edit its own launch video. Anthropic's culture of working (internally) in public is key to their success, as demonstrated by the way they use Claude Tag in their public Slack Channels.

0 views
Filippo Valsorda 3 weeks ago

Opaque, Interoperable Passkey Records (and a Go API)

Passkeys are the most important thing happening in information security right now because they are the only principled solution to the overwhelming effectiveness of phishing attacks. Just like memory safety is the only principled solution to memory corruption attacks. Unfortunately, implementing them on the server side can appear more complex than using password hashes. Part of this is unavoidable because passkeys require interaction with the browser to get their phishing resistance properties. Part of it, however, could be abstracted away a little more effectively by defining interoperable passkey record encodings. The WebAuthn specification defines a credential record as an abstract concept with a number of components such as , , , , , and other flags. Google recommends a database table with a Credential ID primary key, and , , and columns. Adam Langley’s stellar Tour of WebAuthn similarly recommends a primary key, and separate and columns. All guidance recommends using libraries to handle WebAuthn authentication, but that still leaves applications with a potentially non-interoperable database schema. It might also be impractical to defer the whole authentication flow and database interaction to a library or framework. Interoperable, well-specified passkey records that the application can handle as opaque strings, like password hashes, can be a middle-ground abstraction layer. c2sp.org/passkey-record is a specification proposal which borrows the syntax of Password Hashing Competition (PHC) Strings and reuses the existing authenticator data encoding for the bulk of the work. A record looks like this: The payload is the authenticator data, a binary encoding of most of the credential record fields that is already specified by WebAuthn and included in the JSON encoding of an AuthenticatorAttestationResponse (which is the return type of even if attestation is not in use). Transports are the only missing field, and they are stored as PHC parameters. The application is then only in charge of keeping track of what passkey records are associated with a user account, which is a task that is familiar to web developers because it is not dissimilar to implementing password authentication (except there are multiple passkeys per account). These opaque strings can be passed to a library to verify the login assertion (or to generate a registration request with the appropriate ). With a well-specified interoperable storage format, it will hopefully be possible to switch passkey library (or even backend language) while preserving a credentials database. Along with the passkey record, applications might still wish to store metadata fields like a user-selected nickname and creation and last use timestamps, to provide pretty passkey management UIs. None of these require any special treatment by the WebAuthn library. The one exception is the backed up state flag. Passkeys report to the server whether they are backed up (e.g. to iCloud Keychain or a Google account), and servers can use that signal to suggest removing a password from an account. This flag can change across logins, while the passkey record is immutable, so it would have to be stored separately and updated on every login. I think this logic is overrated for the average website, which will keep supporting email password resets anyway. Building on top of these passkey records, I drafted a potential stateless Go package API . The registration flow is The login flow is The application is in charge of The library provides JSON values that can be passed straight to and , and accepts JSON values returned by calling on a PublicKeyCredential . This is optimized for the discoverable credential flow (a.k.a. passkeys, where the authenticator stores and provides to the server the user ID) but the method can also be used for second-factor flows or re-authentication prompts. The API works for both modal and conditional UI (autofill) flows. There are a few helpers to extract information from the passkey record ( , ) and from the JSON-encoded PublicKeyCredential ( ). Currently, there is no implementation; I would like to get feedback on the passkey record format and on the Go API , before potentially making a proposal for Go 1.28. One thing you can’t do with this storage model is ensure that different accounts don’t share passkeys with the same Credential ID, which the spec says you SHOULD do. The reason for that check is avoiding attacks where you look up a credential by its ID and land at the wrong public key or user ID because the attacker intentionally injected a colliding Credential ID through their own account. This attack simply can’t happen if you don’t have a Credential ID index in the first place! The index is only needed to mitigate an attack introduced by the existence of the index. Login attempts carry the user ID, and if you use that to look up the user’s passkey records to verify the login against, it doesn’t matter if some other user has a passkey with the same Credential ID, just like it doesn’t matter if two users share a password. Don’t let the attacker dictate your PRIMARY KEY and you won’t have PRIMARY KEY collision attacks. For more Go API previews, follow me on Bluesky at @filippo.abyssdomain.expert or on Mastodon at @[email protected] . More from this year’s CENTOPASSI (a GPS-tracked motorcycle competition involving careful planning, 100 coordinates, and 1700 km of secondary roads over three and a half days). Here’s a glimpse of Castel del Monte (AQ), after climbing down from a deserted and still snowy Campo Imperatore. My work is made possible by Geomys , an organization of professional Go maintainers, which is funded by Ava Labs , Teleport , Datadog , Tailscale , and Sentry . Through our retainer contracts they ensure the sustainability and reliability of our open source maintenance work and get a direct line to my expertise and that of the other Geomys maintainers. (Learn more in the Geomys announcement .) Here are a few words from some of them! Teleport — For the past five years, attacks and compromises have been shifting from traditional malware and security breaches to identifying and compromising valid user accounts and credentials with social engineering, credential theft, or phishing. Teleport Identity is designed to eliminate weak access patterns through access monitoring, minimize attack surface with access requests, and purge unused permissions via mandatory access reviews. Ava Labs — We at Ava Labs , maintainer of AvalancheGo (the most widely used client for interacting with the Avalanche Network ), believe the sustainable maintenance and development of open source cryptographic protocols is critical to the broad adoption of blockchain technology. We are proud to support this necessary and impactful work through our ongoing sponsorship of Filippo and his team. call with the logged-in (or otherwise identified) user details and any existing passkey records pass the returned JSON to and then to pass the returned JSON-encoded PublicKeyCredential to store the returned passkey record in the database call while generating the log-in page store the returned request in a key-value cache with a short TTL, under , and pass the returned JSON to and then to pass the returned JSON-encoded PublicKeyCredential to , and use the returned requestID to retrieve the request from the key-value cache and use the returned userID to retrieve the passkey records from the database pass the JSON PublicKeyCredential, the request, and the passkey records to associating an opaque, permanent, privacy-preserving user ID with each user; storing passkey records associated with a user; and caching request challenges produced by .

0 views
Unsung 3 weeks ago

The Swiss Cheese model, pt. 1

Have you head of the Swiss Cheese model ? You see it sometimes in descriptions of how complex systems fail. The visual usually goes like this: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-swiss-cheese-model-pt-1/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-swiss-cheese-model-pt-1/1.1600w.avif" type="image/avif"> The whole idea is: even if you have multiple layers of safety – like many slices of cheese – there are always holes in each slice. Typically, a hole in any slice is covered by a non-hole in the previous one or the next one. (For example, a car might not allow you to grab your keys if you have not shifted to park – or, if you start driving with a handbrake on by accident, the car might yell at you.) But occasionally, the holes just happen to line up, and a larger disaster strikes. The model is used in analyses of past accidents, and prevention of future ones. It has proponents and detractors. It’s hard to talk about its applications because the most common examples are horrific. In my book, I wrote about Therac-25 , and that was a really unpleasant chapter to research and to write. Other go-to case studies are equally bleak: Chernobyl , Challenger , the Tenerife airport disaster , the Deepwater Horizon explosion . But I wanted to share it because in my head it applies to UI design also, and sometimes helps me think of how small details add up to a larger whole. In this first part, let’s start with a more traditional example, although a non-drastic one. Here’s a story of Knight Capital Group, a financial services and trading firm. I’m going to hand off the summary of the accident to Henrico Dolfing : On the morning of August 1, 2012, Knight Capital Group opened its systems for what should have been a routine trading day, yet within minutes the firm began sending a flood of unintended orders into the U.S. equity market, buying high and selling low across dozens of stocks in a pattern that made no economic sense and could not be stopped through normal controls. What initially appeared as unusual market activity quickly escalated into a systemic failure inside one of the largest market makers in the United States, with algorithms behaving in ways that neither traders nor engineers could fully understand in real time. […] By the time the issue was identified and the system shut down roughly 45 minutes later, Knight had generated more than 4 million executions across 154 stocks, covering approximately 397 million shares, and accumulated positions worth billions of dollars, resulting in losses of more than $460 million […]. The scale of the incident was not only financial but structural, as a single deployment failure had propagated through a system responsible for a meaningful share of U.S. equity trading. […] Here’s what happened. Long ago, the firm built a pretty boring function called Power Peg to automate some transactions. The function used a standard shared limiter that made it stop executing when all the required transactions were fulfilled. After some years in use, the function was deprecated in 2003 and stopped being used then, but crucially, the code was never actually removed. Some time in between 2003 and 2012, the limiter functionality was upgraded, and the old code stopped being compatible with it. All code in production was rewritten to use the new limiter, but the Power Peg feature wasn’t, as it was already deprecated and not in use. In 2012, the firm started writing a new program for automated transactions. Its creators decided to reuse the same software flag that previously activated Power Peg. The existence of the old code was known at the time, and the idea was that the code would be overwritten by the new program, so the reused flag would only trigger new code. In July 2012, the new code was finished and the firm started to install it on all the servers, overwriting Power Peg. The firm intended to deploy the new code to all eight servers, but a mistake resulted in it only arriving on seven servers. No one caught that mistake. At this point, you can piece it all together. At this point, I imagine many of you have been wincing more and more with each passing paragraph. On August 1, the flag for the new functionality was turned on. Everything was fine on seven servers, but on the eighth one, the flag reawakened dormant code that immediately started executing. As the old code was not compatible with the new limiter, it was never limited, cascading into millions of transactions in less than an hour, and a lot of collateral damage; the subsequent market reaction to the news of the firm losing over $400 million and caused its own stock to tank. Oh yeah, I didn’t mention this yet – the failure killed the firm. (Well, it resulted in a merger, but that seemed to be a way to save face after Knight Capital Group almost went under.) How does the Swiss Cheese apply to this? You can see it as five holes in five different slices of cheese: What’s important to understand about this model is that either of these in isolation would objectively be a small mistake, and caught by the other slides. As a matter of fact, any four of these happening would still not add up to a catastrophe. But in this case, the five mistakes lined up perfectly. Many analyses of such accidents blame a single event in the chain – in this case often the sysadmin that didn’t deploy the code to eight servers properly – but this is primarily because we like stories of individual agency, and are not well equipped to understand stories of systems . (Even Star Trek added a Borg Queen, after all.) That’s the accident eventually dubbed “a Knightmare.” More examples of systems closer to our hearts in following parts. #bugs #definitions it was a mistake to not actually remove the old code it was a mistake to reuse the same flag it was a mistake to not deploy to all 8 servers it was a mistake to not have a procedure for someone to double check the deployment it was a mistake to not have a way of auto-detecting (and perhaps auto-stopping) the runaway processes when the regular limiter failed

0 views

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 1 months 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 1 months 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 months 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 months 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 1 months 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 1 months 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 1 months 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