<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vincent Tran</title>
    <description>The latest articles on DEV Community by Vincent Tran (@0xgosu).</description>
    <link>https://dev.to/0xgosu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2435529%2F3d8e6fd3-a6ef-41e2-bcf7-dbc8d04d17e1.jpeg</url>
      <title>DEV Community: Vincent Tran</title>
      <link>https://dev.to/0xgosu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0xgosu"/>
    <language>en</language>
    <item>
      <title>Zig 0.17: A Release Guide for Builders and Migrators</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/zig-017-a-release-guide-for-builders-and-migrators-3apj</link>
      <guid>https://dev.to/0xgosu/zig-017-a-release-guide-for-builders-and-migrators-3apj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqwgjgqac7td1402zm7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqwgjgqac7td1402zm7j.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zig 0.17.0 is a substantial release disguised as a short cycle. The &lt;a href="https://ziglang.org/download/0.17.0/release-notes.html" rel="noopener noreferrer"&gt;official release notes&lt;/a&gt; count five months, 206 contributors, and 925 commits. The central change is architectural: a build script now &lt;strong&gt;configures&lt;/strong&gt; a build, while a separate, reusable program &lt;strong&gt;makes&lt;/strong&gt; it. Around that boundary, Zig added a protocol for tools to inspect and control a build, improved incremental compilation on Linux, expanded its self-hosted linkers, and made several language rules more precise.&lt;/p&gt;

&lt;p&gt;Those changes affect different users in different ways. A developer running &lt;code&gt;zig build&lt;/code&gt; may see less repeated setup work. An editor can eventually consume a structured build graph. A project moving from 0.16 must audit a few semantic changes that may compile successfully yet behave differently. This guide follows the work from source code through build execution, then covers the migration points and current limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build configuration stops being the build engine
&lt;/h2&gt;

&lt;p&gt;Previously, the build runner executed &lt;code&gt;build.zig&lt;/code&gt; logic as part of each &lt;code&gt;zig build&lt;/code&gt; invocation. That made the build script and the engine that executed its graph tightly coupled. In 0.17, Zig compiles the project-specific &lt;strong&gt;configurer&lt;/strong&gt; separately from the general &lt;strong&gt;maker&lt;/strong&gt;. The maker is built once after installation and can be reused even when &lt;code&gt;build.zig&lt;/code&gt; changes. The project script produces a compact serialized configuration; the maker consumes it and performs package and build-graph work.&lt;/p&gt;

&lt;p&gt;This separation matters because editing a build script no longer forces the generic engine to be rebuilt. The maker can be optimized for repeated work, including watch and fuzz workflows. Zig can also skip configuration for some invocations. The release notes do not promise a universal speedup: the benefit depends on how often a project rebuilds, what changed, and which command is run.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fzig-017-build-system%2Fbuild-boundary.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fzig-017-build-system%2Fbuild-boundary.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;The configurer describes the work. The reusable maker executes it and can expose the graph to external tools.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;The configuration is a new interface, not just an internal cache file. &lt;code&gt;zig build --print-configuration&lt;/code&gt; can render it as ZON for inspection. With &lt;code&gt;--listen=-&lt;/code&gt;, the build system serves the &lt;a href="https://ziglang.org/download/0.17.0/release-notes.html#Build-Server-Protocol" rel="noopener noreferrer"&gt;build server protocol&lt;/a&gt;. A client can read the static graph after configuration, observe step start and completion events and generated files, and request individual steps. Some information, including the complete exposed module set, is still missing. The project expects future first-party tooling to use the same interface.&lt;/p&gt;

&lt;p&gt;This architecture comes with a real editor regression. ZLS relied on the old ability to fork the build runner, and that path disappears in 0.17. The Zig and ZLS teams are working on the new protocol, but the release notes explicitly say the separation prevents ZLS from working with 0.17.0 as before. Developers who depend on language-server features should check ZLS compatibility before upgrading their daily toolchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache and package work move to the right layer
&lt;/h2&gt;

&lt;p&gt;The cache now tracks directories as well as files. Adding, removing, or renaming an entry can invalidate a build correctly. It also has a metadata mode that treats changes to size, inode, or modification time as a miss even when file contents are unchanged. Those choices let a build step express what it actually depends on instead of approximating a directory as a list of known files. Cache records also switch to a binary format; the release notes report roughly &lt;strong&gt;25% smaller files&lt;/strong&gt;. &lt;code&gt;zig cache-cat&lt;/code&gt; offers a way to inspect them.&lt;/p&gt;

&lt;p&gt;Package management has moved out of the compiler executable and into the build system. That includes &lt;code&gt;zig build&lt;/code&gt;, &lt;code&gt;zig fetch&lt;/code&gt;, &lt;code&gt;zig init&lt;/code&gt;, &lt;code&gt;zig libc&lt;/code&gt;, and &lt;code&gt;zig cache-cat&lt;/code&gt;, along with fetching, networking, compression, and package-manifest parsing code. It makes the compiler binary less responsible for operations that belong to the build layer. There are visible behavior changes: &lt;code&gt;zig fetch&lt;/code&gt; still populates the global cache, while &lt;code&gt;--save&lt;/code&gt; also writes to the local package path; &lt;code&gt;zig build&lt;/code&gt; fetches locally as well. The &lt;code&gt;--pkg-path&lt;/code&gt; option and &lt;code&gt;ZIG_LOCAL_PKG_DIR&lt;/code&gt; now apply to both fetch and build.&lt;/p&gt;

&lt;p&gt;Build-script APIs also move with this boundary. &lt;code&gt;b.build_root&lt;/code&gt; becomes &lt;code&gt;b.root&lt;/code&gt;, several artifact and file argument helpers gain &lt;code&gt;2&lt;/code&gt; replacements, and a &lt;code&gt;LazyPath&lt;/code&gt; basename is no longer available during configuration because its value is only known when the maker runs. A run step that formerly inspected &lt;code&gt;b.args&lt;/code&gt; should use &lt;code&gt;run_cmd.addPassthruArgs()&lt;/code&gt;. That removes the script’s ability to inspect those arguments, but changing them no longer requires rebuilding the script. The &lt;a href="https://ziglang.org/download/0.17.0/release-notes.html#Build-System" rel="noopener noreferrer"&gt;release notes’ build-system section&lt;/a&gt; is the checklist for projects with custom &lt;code&gt;build.zig&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental builds need both compiler and linker cooperation
&lt;/h2&gt;

&lt;p&gt;Incremental compilation is useful only if changed source can be analyzed, generated, and linked without starting from scratch. Zig 0.16 made progress on the compiler side; 0.17 fixes more correctness and dependency issues and makes its newer ELF linker capable enough for most &lt;code&gt;x86_64-linux&lt;/code&gt; projects. The documented workflow is &lt;code&gt;zig build -fincremental --watch&lt;/code&gt;: Zig watches source files and rebuilds the affected work after a change.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fzig-017-build-system%2Fincremental-loop.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fzig-017-build-system%2Fincremental-loop.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;An edit can take the short path through changed work, while the existing build graph and unaffected outputs remain reusable.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;The new ELF linker gained x86-64 and SPARC64 support, static and shared libraries, symbol versioning, DWARF information, and other pieces needed for real applications. It is &lt;strong&gt;not yet the default for ordinary builds&lt;/strong&gt; , because it has not reached full feature parity with the legacy linker. Incremental mode enables it by default for supported projects. That distinction is important: “works for most Linux projects in incremental mode” is narrower than “all Zig builds now use the new linker.” Future work includes a Mach-O linker, improved AArch64 support, and incremental builds without &lt;code&gt;--watch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Other backends moved forward without becoming universal defaults. The self-hosted SPIR-V backend is now multithreaded and supports task and mesh shader calling conventions; its linker was rewritten to accept external SPIR-V objects and incremental compilation. The WebAssembly backend passes the release’s behavior-test comparison against LLVM, but lacks the debug information needed to become the debug-mode default. An initial LoongArch backend is experimental and not yet usable. On Windows, COFF linker support expanded across objects, libraries, imports, TLS, and several linking conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language changes deserve an audit, not a blind search and replace
&lt;/h2&gt;

&lt;p&gt;The most consequential semantic change is &lt;code&gt;@bitCast&lt;/code&gt;. Zig now defines it in terms of a value’s &lt;strong&gt;logical bits&lt;/strong&gt; , rather than the target’s memory layout. For arrays and vectors, element bit sequences are concatenated in order. This makes the operation consistent across endian targets, but code casting arrays or vectors can change behavior &lt;strong&gt;without a compiler error&lt;/strong&gt;. Casts involving &lt;code&gt;extern struct&lt;/code&gt; and &lt;code&gt;extern union&lt;/code&gt; are no longer allowed. If the intent is to reinterpret in-memory representation, the migration path is &lt;code&gt;@ptrCast&lt;/code&gt; or an &lt;code&gt;extern union&lt;/code&gt;; if the intent is value-bit reinterpretation, review the result on big- and little-endian targets.&lt;/p&gt;

&lt;p&gt;Several smaller rules tighten the language. &lt;code&gt;@hasDecl&lt;/code&gt; now reports only public declarations, so reflection code that tests private members may take a different branch. Array multiplication syntax and &lt;code&gt;void{}&lt;/code&gt; are removed; use the new explicit forms documented in the notes. The &lt;code&gt;errdefer |err|&lt;/code&gt; capture is gone, with an outer &lt;code&gt;catch&lt;/code&gt; and inner function suggested where the error name must be logged. Zig added &lt;code&gt;@divCeil&lt;/code&gt;, &lt;code&gt;@backingInt&lt;/code&gt;, and &lt;code&gt;@fromBackingInt&lt;/code&gt;; &lt;code&gt;@SpirvType&lt;/code&gt; exposes SPIR-V-only types to normal declarations. The grammar is now formally specified and fuzzed, while C translation is moving to an external package.&lt;/p&gt;

&lt;p&gt;The standard library has its own migration surface. &lt;code&gt;fmt.allocPrint&lt;/code&gt; moved to the allocator, &lt;code&gt;std.gpu&lt;/code&gt; became &lt;code&gt;std.spirv&lt;/code&gt;, and &lt;code&gt;std.builtin&lt;/code&gt; is deprecated in favor of &lt;code&gt;std.lang&lt;/code&gt;. &lt;code&gt;std.mem.eql&lt;/code&gt; and &lt;code&gt;findDiff&lt;/code&gt; now handle floating-point NaNs according to value comparison instead of assuming identical memory means equality. URI parsing was decoupled from host-name validation: callers that need a validated host should use &lt;code&gt;HostName.fromUri&lt;/code&gt; and handle its error set. These are behavior and API changes; a successful compile is not a substitute for tests around reflection, bit representations, and parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Toolchain upgrades and known limits
&lt;/h2&gt;

&lt;p&gt;Zig 0.17 ships LLVM &lt;strong&gt;22.1.8&lt;/strong&gt; and updated C toolchain components and system headers. One LLVM loop-vectorization pass remains disabled because the fix for a known miscompilation is not in LLVM 22; the project expects to restore it with LLVM 23 in a later release. The release notes also call out known regressions in soft-float x86, &lt;code&gt;std.debug.simple_panic&lt;/code&gt;, weak libc symbol overrides, some response-file run steps, and SPIR-V. Zig continues to describe itself as pre-1.0 software with known bugs and possible miscompilations. For a production codebase, upgrading should mean pinning 0.17 in CI, running the project’s target matrix, and testing the exact editor and linker workflow it uses.&lt;/p&gt;

&lt;p&gt;Target support is broader than the Linux incremental-build story. &lt;code&gt;aarch64-openbsd&lt;/code&gt; now runs natively in CI, and FreeBSD and NetBSD on AArch64 are tested on pull requests. Zig worked around an LLVM problem affecting AArch64 Windows binaries, added stack traces for 32-bit ARM and SPARC crashes, and improved unwinding with pointer authentication on AArch64. SPARC64 Linux is described as generally usable, helped by the newer ELF linker. There is new LoongArch32 Linux target support, early Xtensa Linux work, and target metadata for several consoles. These additions do not all carry the same promise: Zig’s &lt;a href="https://ziglang.org/download/0.17.0/release-notes.html#Tier-System" rel="noopener noreferrer"&gt;support tiers&lt;/a&gt; distinguish tested standard-library and libc behavior from targets that can only emit machine code or C/assembly. Check the specific target row before treating a cross-compile as a supported deployment.&lt;/p&gt;

&lt;p&gt;The C-facing toolchain also changes. The release supplies musl 1.2.5 with backported fixes, glibc 2.44 for cross-compilation, Linux 7.2 and macOS 27 headers, and newer NetBSD and OpenBSD libc versions. More libc functionality is provided by Zig’s own implementation rather than copied vendor source. On PowerPC, Zig now enforces IEEE &lt;code&gt;long double&lt;/code&gt;; GNU EABI targets that require the IBM double-double format are dropped, while corresponding musl targets remain. These details matter to users who rely on Zig primarily as a cross-compiler for C and C++, not only to Zig-language projects.&lt;/p&gt;

&lt;p&gt;One part of the release deliberately stands still: the integrated fuzzer itself received no direct changes. Linker tests are moving toward snapshots of object-file output combined with running artifacts and checking expected errors. &lt;code&gt;zig objdump&lt;/code&gt; gained selective output and redaction useful for those tests, while &lt;code&gt;zig fmt --complexity&lt;/code&gt; exposes token and syntax-node counts to help measure source complexity. Windows resource compilation is being prepared for an official external build package, so related &lt;code&gt;std.Build&lt;/code&gt; APIs are deprecated ahead of that move.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes after 0.17
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://ziglang.org/download/0.17.0/release-notes.html#Roadmap" rel="noopener noreferrer"&gt;project roadmap&lt;/a&gt; connects the unfinished pieces. The immediate editor task is to make the build server protocol cover ZLS’s needs. The compiler team also wants the x86-64 Windows and AArch64 self-hosted backends ready as defaults, more complete linkers with less reliance on LLD, and a stronger integrated fuzzer. Longer-running work includes settling language decisions, completing package-management features, auditing the standard library, and changing the LLVM relationship from an in-process library dependency toward a Clang process dependency. These are plans rather than 0.17 guarantees.&lt;/p&gt;

&lt;p&gt;The larger direction is clear. Zig is making the build graph a durable object that can be cached, inspected, and driven by other programs. The compiler and self-hosted linker are closing the loop from a source edit to a small rebuild. The language is also settling decisions ahead of 1.0. Zig 0.17 makes each of those paths more concrete, while leaving enough compatibility and platform gaps that migration still needs deliberate testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/download/0.17.0/release-notes.html" rel="noopener noreferrer"&gt;Zig 0.17.0 release notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/download/" rel="noopener noreferrer"&gt;Zig downloads and signed release packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/devlog/2026/" rel="noopener noreferrer"&gt;Zig development log, 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/download/0.16.0/release-notes.html" rel="noopener noreferrer"&gt;Zig 0.16.0 release notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>How Rust Made Its Compiler Faster While Changing Its Core</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/how-rust-made-its-compiler-faster-while-changing-its-core-cif</link>
      <guid>https://dev.to/0xgosu/how-rust-made-its-compiler-faster-while-changing-its-core-cif</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7id7rf8e5hdznx2fhvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7id7rf8e5hdznx2fhvv.png" width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rust’s compiler is changing two of its hardest internal systems while its engineers are making ordinary builds faster. &lt;a href="https://nnethercote.github.io/2026/09/30/how-to-speed-up-the-rust-compiler-in-september-2026.html" rel="noopener noreferrer"&gt;Nicholas Nethercote’s September performance report&lt;/a&gt; puts numbers on that work: across 629 benchmark measurements between July 29 and September 28, 555 improved and 74 regressed. Mean wall time fell &lt;strong&gt;4.57%&lt;/strong&gt;. A few crates saw much larger gains.&lt;/p&gt;

&lt;p&gt;That average is useful, but it hides the real story. Some wins came from a newer LLVM or a better-trained Clippy binary. Others came from removing allocations in frequently visited compiler paths. The most striking case came from changing the order in which a dataflow analysis revisits blocks. Meanwhile, Rust enabled a more precise borrow checker and a new trait solver on nightly, each of which brought its own performance problems to solve.&lt;/p&gt;

&lt;p&gt;These figures describe Rust’s benchmark suite over that interval. They are not a promise that every project will compile 4.57% faster, nor can every gain be added together as an independent percentage. Different changes affect different crates, build modes, and compiler phases.&lt;/p&gt;

&lt;h2&gt;
  
  
  A broad gain, assembled from many small changes
&lt;/h2&gt;

&lt;p&gt;The compiler does more than translate source into machine code. It parses and lowers syntax, resolves types and traits, checks borrows, runs MIR analyses, and eventually asks LLVM to optimize and generate code. Clippy and rustdoc exercise related but distinct paths. A change in one layer can help a particular workload while barely moving another.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Frust-compiler-september-2026%2Foptimization-map.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Frust-compiler-september-2026%2Foptimization-map.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;There was no single speed switch. Different improvements reduced work in different compiler paths.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/rust-lang/rust/pull/158734" rel="noopener noreferrer"&gt;LLVM 23 update&lt;/a&gt; delivered a &lt;strong&gt;1.2% mean wall-time reduction&lt;/strong&gt; across the compiler benchmarks on its own. That is a substantial result for a dependency upgrade, though it does not explain every September improvement. At the tooling edge, Rust’s build process began applying &lt;strong&gt;profile-guided optimization (PGO) to Clippy&lt;/strong&gt;. PGO builds a program using profiles gathered from representative executions, allowing the optimizer to spend effort on paths the tool actually takes. Nethercote reports Clippy wall-time improvements across most of its benchmarks, reaching &lt;strong&gt;18% in the best case&lt;/strong&gt;. That figure is a best case for Clippy, not for all Rust builds.&lt;/p&gt;

&lt;p&gt;The rustdoc gains discussed in an earlier report also had a follow-up explanation from &lt;a href="https://noahlev.org/blog/2026/08/27/making-rustdoc-faster/" rel="noopener noreferrer"&gt;Noah Lev&lt;/a&gt;. They belong to the same larger effort: measure a real workload, find a repeated cost, and remove it without weakening the tool’s behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  New semantics have a performance bill
&lt;/h2&gt;

&lt;p&gt;Rust enabled &lt;strong&gt;Polonius Alpha&lt;/strong&gt; , its next borrow checker, on nightly. It is more precise about when a borrow is live along a particular control-flow path. The &lt;a href="https://blog.rust-lang.org/2026/08/04/enabling-polonius-alpha-on-nightly/" rel="noopener noreferrer"&gt;Rust team’s announcement&lt;/a&gt; gives a useful example: a map lookup can return a mutable reference in one match arm while the other arm inserts a default value. The older analysis can treat the returned borrow as live across both arms and reject valid code. Polonius Alpha understands that the borrow from the first arm does not exist on the insertion path.&lt;/p&gt;

&lt;p&gt;That precision takes work. Most measured crates have little change, but some see regressions, including &lt;code&gt;serde&lt;/code&gt;. Jack Huey made liveness calculations lazy, reducing instruction counts for &lt;code&gt;serde&lt;/code&gt; by &lt;strong&gt;3–5%&lt;/strong&gt; and producing smaller gains elsewhere. A data-structure adjustment and inlining changes trimmed more work across other benchmarks. The project still has slower outliers to investigate. These are nightly changes under evaluation, not a claim that the new checker is already faster for every crate.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;next-generation trait solver&lt;/strong&gt; also became the nightly default. A trait solver proves obligations such as whether a type implements a trait and what an associated type resolves to. The &lt;a href="https://blog.rust-lang.org/2026/08/21/enabling-next-solver-on-nightly/" rel="noopener noreferrer"&gt;Rust team’s release note&lt;/a&gt; calls this a major replacement of the compiler’s type reasoning machinery. It fixes long-standing behavior and prepares future language features, but its early implementations could perform much worse on certain trait-heavy crates.&lt;/p&gt;

&lt;p&gt;Work by Jana Dönszelmann, Nethercote, and others attacked those outliers. Several targeted pull requests cut compile time for individual stress cases by &lt;strong&gt;15%, 25%, or 50%&lt;/strong&gt; , while &lt;a href="https://donsz.nl/blog/new-solver-performance" rel="noopener noreferrer"&gt;Dönszelmann’s account&lt;/a&gt; explains the broader solver effort. Those numbers refer to particular cases; they should not be presented as suite-wide savings. They matter because a new default is only practical if pathological costs are brought under control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing work from hot paths
&lt;/h2&gt;

&lt;p&gt;Many compiler improvements are less dramatic to describe but easier to repeat. A specialization-graph change reduced mean cycle counts by &lt;strong&gt;1.58%&lt;/strong&gt; over the benchmark suite. Another change made loading incremental compilation data cheaper, with instruction-count wins of up to &lt;strong&gt;6%&lt;/strong&gt; in individual measurements. Avoiding allocations while processing obligations reached &lt;strong&gt;2%&lt;/strong&gt; in the best case. Replacing dynamic dispatch with static dispatch in the selection between old and new trait solvers removed another frequently observed allocation path, mostly yielding gains below 1%.&lt;/p&gt;

&lt;p&gt;The pattern is important. A tiny allocation in cold initialization code barely matters. The same allocation inside a path visited for thousands of obligations can dominate a profile. A change that seems small in source code can therefore move a whole-suite metric when it sits at the right frequency. Conversely, a good-looking microbenchmark can fail to move actual compile time if its path is rarely used. That is why the report distinguishes &lt;strong&gt;instruction counts&lt;/strong&gt; , &lt;strong&gt;CPU cycles&lt;/strong&gt; , and &lt;strong&gt;wall time&lt;/strong&gt; instead of treating them as interchangeable.&lt;/p&gt;

&lt;p&gt;There were smaller wins in other corners. Rust increased the compiler’s default stack size so it could remove some manual stack-extension machinery from recursion-heavy paths; instruction counts fell across many benchmarks, by almost &lt;strong&gt;3%&lt;/strong&gt; in the best case. A cleanup in lowering the abstract syntax tree to the high-level intermediate representation unexpectedly cut instruction counts by up to &lt;strong&gt;1.5%&lt;/strong&gt;. The compiler team also sometimes merged several performance PRs as a rollup to manage CI capacity, then ran per-change performance measurements afterward so unexpected effects could still be found.&lt;/p&gt;

&lt;p&gt;This work involved many contributors. Nethercote singled out new contributor xmakro for several of the hot-path changes. He also described using language models for private analysis on some PRs while writing his own code and prose, consistent with the &lt;a href="https://forge.rust-lang.org/policies/llm-usage.html" rel="noopener noreferrer"&gt;Rust project’s LLM usage policy&lt;/a&gt;. The report closes with his move to work on the project’s &lt;a href="https://goals.rust-lang.org/2026/compiler-performance-optimization.html" rel="noopener noreferrer"&gt;compiler performance goal&lt;/a&gt; at Hexcat. Those details underline that the result is an ongoing engineering program, not a one-off optimization sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  One traversal change, 1.4 million fewer block visits
&lt;/h2&gt;

&lt;p&gt;The clearest illustration of algorithmic cost is &lt;a href="https://github.com/rust-lang/rust/pull/160193" rel="noopener noreferrer"&gt;the control-flow graph traversal change&lt;/a&gt;. A dataflow analysis keeps facts about a program at each basic block. It repeatedly processes blocks until those facts stop changing: a &lt;strong&gt;fixpoint&lt;/strong&gt;. The order of processing can decide how often the same block must be revisited.&lt;/p&gt;

&lt;p&gt;Rust’s old worklist began in a sensible order, but when a loop sent new information backward it appended dirtied blocks to a first-in, first-out queue. Successors could then be processed before the earlier block had settled, causing another round of work later. The new algorithm selects the earliest dirty block in dataflow order. It aims to propagate changed input through the graph before spending time on downstream blocks whose inputs are likely to change again.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Frust-compiler-september-2026%2Fdataflow-traversal.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Frust-compiler-september-2026%2Fdataflow-traversal.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Processing the earliest dirty block first avoids much repeated work in one unusually large control-flow graph.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;For a &lt;code&gt;cranelift-codegen&lt;/code&gt; function with more than &lt;strong&gt;18,000 basic blocks&lt;/strong&gt; , the &lt;code&gt;EverInitializedPlaces&lt;/code&gt; analysis previously called &lt;code&gt;apply_effects_in_block&lt;/code&gt; about &lt;strong&gt;1.5 million times&lt;/strong&gt; before reaching a fixpoint. The new traversal needed about &lt;strong&gt;90,000 calls&lt;/strong&gt;. Nethercote reports about a &lt;strong&gt;30% wall-time reduction&lt;/strong&gt; for a &lt;code&gt;check&lt;/code&gt; build of that crate. The pull request’s measurements also show that the biggest gain was concentrated in that particular analysis. Most ordinary functions are small enough that the new ordering makes little difference. This is a case where one outlier exposed avoidable work in a general algorithm.&lt;/p&gt;

&lt;p&gt;A second change stopped &lt;code&gt;EverInitializedPlaces&lt;/code&gt; from tracking unnecessary data for projections. That reduced instruction counts on the &lt;code&gt;match-stress&lt;/code&gt; benchmark by &lt;strong&gt;17%&lt;/strong&gt; , with smaller effects elsewhere. The two fixes illustrate different levers: do fewer iterations, and make each remaining iteration cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the measurements actually tell us
&lt;/h2&gt;

&lt;p&gt;Compiler performance is a distribution. The 4.57% mean improvement and the 555-to-74 split indicate broad progress, while the double-digit examples explain where engineering effort had unusually high payoff. The remaining regressions matter too, particularly for new compiler machinery that expands what Rust can accept. A fast average would be much less reassuring if a widely used crate became prohibitively slow.&lt;/p&gt;

&lt;p&gt;The most useful lesson from this period is methodological. Rust’s team paired large semantic changes with benchmark runs, inspected specific slow crates, and kept cutting costs in the paths that profiles showed were hot. Some changes improved a whole suite; others rescued a single pathological case. Together they made the compiler faster during a period when it was also becoming more capable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nnethercote.github.io/2026/09/30/how-to-speed-up-the-rust-compiler-in-september-2026.html" rel="noopener noreferrer"&gt;Nicholas Nethercote, &lt;em&gt;How to speed up the Rust compiler in September 2026&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://perf.rust-lang.org/compare.html?end=c1070d69382b8d2f2eb65119c738a77d9e324c9e&amp;amp;nonRelevant=true&amp;amp;start=1a833e16546c2eb012758ddd499964fd8afee29e&amp;amp;stat=wall-time&amp;amp;tab=compile" rel="noopener noreferrer"&gt;Rust compiler performance comparison, July 29–September 28&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rust-lang/rust/pull/160193" rel="noopener noreferrer"&gt;Rust pull request: improved control-flow graph traversal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rust-lang.org/2026/08/04/enabling-polonius-alpha-on-nightly/" rel="noopener noreferrer"&gt;Rust project: Polonius Alpha on nightly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.rust-lang.org/2026/08/21/enabling-next-solver-on-nightly/" rel="noopener noreferrer"&gt;Rust project: next-generation trait solver on nightly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://donsz.nl/blog/new-solver-performance" rel="noopener noreferrer"&gt;Jana Dönszelmann, &lt;em&gt;Optimizing the new trait solver&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rust-lang/rust/pull/159642" rel="noopener noreferrer"&gt;Rust pull request: profile-guided Clippy optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>The Request Path Behind Netlify's Faster Edge Functions</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Thu, 01 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/the-request-path-behind-netlifys-faster-edge-functions-2a3j</link>
      <guid>https://dev.to/0xgosu/the-request-path-behind-netlifys-faster-edge-functions-2a3j</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkk6jfpwb70kcfwemfri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkk6jfpwb70kcfwemfri.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An edge function sits directly in a website’s request path. It can choose a route, check authorization, or tailor a response before the page reaches the visitor. That position makes small infrastructure costs visible: every extra trip and every slow startup delays the response. &lt;a href="https://www.netlify.com/blog/edge-functions-firecracker-microvms/" rel="noopener noreferrer"&gt;Netlify’s engineering account&lt;/a&gt; describes a migration from a hosted V8-isolate service to Firecracker microVMs running inside Netlify’s edge network. The interesting part is how many decisions must line up for a virtual machine to be the &lt;em&gt;faster&lt;/em&gt; option.&lt;/p&gt;

&lt;p&gt;Netlify reports roughly &lt;strong&gt;one billion Edge Function executions per day&lt;/strong&gt;. On its new platform, a warm invocation’s overhead—routing to compute, entering the microVM, running the function, and producing response headers—is about &lt;strong&gt;5–6 ms at the median&lt;/strong&gt; , versus &lt;strong&gt;25–40 ms&lt;/strong&gt; previously. It also reports a 47.4% improvement at p99, 99.998% availability, and logs arriving about five times faster. Those are production figures supplied by Netlify, not independent benchmarks, and the comparison is of complete serving paths rather than an isolate and a VM in isolation. The previous path sent matching requests to an external execution provider; the new path stays within Netlify’s network. That removed network and control-plane work is central to the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  A request becomes a placement decision
&lt;/h2&gt;

&lt;p&gt;The request first reaches a nearby edge node. That node terminates TLS and checks the path against the deploy’s Edge Function routes. If no route matches, normal cache and origin handling continues. If one does, the edge node prepares a machine specification and sends the request to compute capacity in the region. The specification identifies three images: the JavaScript runtime, Netlify’s platform layer, and the customer’s function bundle. It also sets CPU, memory, and connection limits.&lt;/p&gt;

&lt;p&gt;The edge node computes a service identity from the specification hash and site-specific data. A new deployment or a different environment configuration therefore becomes a different service. This matters for both correctness and isolation: code from two deployments should not accidentally share a running machine, and an untrusted function should not reach another customer’s execution environment. A microVM provides a hardware virtualization boundary around each function service; it does not, by itself, prove the entire platform secure. The host, control plane, image supply chain, and network policy still matter.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fnetlify-edge-microvms%2Frequest-path.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fnetlify-edge-microvms%2Frequest-path.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;The fast path keeps routing and execution inside Netlify’s network; image fetches happen on cache misses.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Each region has several compute nodes. Netlify uses &lt;strong&gt;rendezvous hashing&lt;/strong&gt; to steer a given service toward the same node. Repeated requests are likely to find the service’s code cached and a ready execution path, reducing needless cold starts. Pure stickiness has a cost: one popular service can saturate its assigned node and harm its neighbors. When traffic crosses a threshold, Netlify spreads that service across a slice of nodes. That trades some cache locality for capacity and keeps a single hot workload from pinning a whole region. The placement policy is part of the latency story, not just a load-balancer detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  A first request pays for the image
&lt;/h2&gt;

&lt;p&gt;A compute node receiving the request looks for an existing service ID. If the service is absent, it creates one and checks whether the requested images are already on disk. Missing images are fetched from the edge node. Later requests reuse the local copies. This means a new deploy does not need to push every customer’s code to every region in advance. A region downloads the images it actually needs, when traffic arrives there.&lt;/p&gt;

&lt;p&gt;Netlify says this image-fetching cold path occurs on about &lt;strong&gt;1.2% of invocations&lt;/strong&gt; and takes about &lt;strong&gt;9 ms on average&lt;/strong&gt;. Those figures describe its observed workload; they do not promise that every first request will finish in nine milliseconds. The image may already be cached on some nodes, and the total user response time still includes the function’s own work and any origin or network calls it makes. At a billion daily invocations, even a small cold fraction is operationally substantial, so caching and placement need to work under real traffic rather than only in a benchmark.&lt;/p&gt;

&lt;p&gt;The three-image split has another operational advantage. Netlify can update the runtime or platform layer separately from each customer’s code. The spec carried with a request says exactly which combination should run. The service ID then makes a changed combination distinct from the previous one. This is a useful deployment property: an old function cannot silently turn into a new version merely because a file was replaced under a warm process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What starts inside the microVM
&lt;/h2&gt;

&lt;p&gt;Netlify says a microVM can be created in under a millisecond and started in about &lt;strong&gt;2 ms at p99&lt;/strong&gt;. It uses a minimal Linux environment rather than booting a general-purpose operating system. The customer files are mounted as an uncompressed EROFS image and memory-mapped. The VM can read the needed pages as execution reaches them rather than copying the whole bundle into memory at launch.&lt;/p&gt;

&lt;p&gt;Once the JavaScript server has booted and opened its port, the platform takes a snapshot. Idle microVMs can scale to zero; a later invocation restores a machine from the snapshot. The snapshot is also memory-mapped, so resuming does not require eagerly reading its full contents. Unikraft, which collaborated on the platform, describes additional work around &lt;a href="https://unikraft.com/blog/netlify-edge-functions" rel="noopener noreferrer"&gt;on-demand image resolution and VM lifecycle&lt;/a&gt;. Together, small boot images, lazy file access, snapshots, and placement make VM startup fit an edge request budget. A tiny VM with a slow image-distribution system would not produce the same result.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fnetlify-edge-microvms%2Finstance-lifecycle.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fnetlify-edge-microvms%2Finstance-lifecycle.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;A snapshot is the bridge between scaling idle capacity to zero and serving the next request quickly.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;The platform does not keep a microVM alive indefinitely. Netlify caps how many requests one instance handles and starts another in anticipation of retirement. That keeps long-lived process drift bounded while maintaining warm capacity. On the compute node, local DNS resolvers remove another avoidable hop. Metrics separate boot time, first port open, and user-code start so the team can see &lt;em&gt;where&lt;/em&gt; delay occurs. Circuit breakers reroute traffic and decommission unhealthy nodes. These details matter because a six-millisecond median is not enough if a failure leaves requests stranded or tail latency grows unchecked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability requires a separate control plane
&lt;/h2&gt;

&lt;p&gt;Edge and compute nodes have different jobs. Edge nodes handle incoming traffic and route matching; compute nodes run customer code. Netlify builds them separately, allowing each fleet to use suitable machine types and scale independently. A control plane tracks healthy compute nodes and publishes that list to edge nodes. For a rollout, a new compute fleet comes up alongside the old one, reaches the desired size, and takes traffic only after health checks pass. The same separation makes rollback possible without asking every request handler to change in place.&lt;/p&gt;

&lt;p&gt;The system also changed the operational loop for developers. &lt;a href="https://unikraft.com/blog/netlify-edge-functions" rel="noopener noreferrer"&gt;Unikraft reports&lt;/a&gt; median log delivery falling from about 2.5 seconds to 500 ms, including Netlify’s enrichment pipeline. That is distinct from request latency, but it shortens the time between a deploy behaving badly and a developer seeing evidence. Netlify says the migration is already live, at the same pricing, with no project changes required. Existing Edge Function declarations, URL imports, npm packages, Node built-ins, and local development continue to work as before.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new foundation, with current limits still in place
&lt;/h2&gt;

&lt;p&gt;Running a full VM and owning the runtime gives Netlify room to change what Edge Functions can support. Its team points to broader npm compatibility, revisiting old execution limits, and features that depend on controlling the network path. Those are &lt;strong&gt;possibilities&lt;/strong&gt; , not all shipped changes. &lt;a href="https://docs.netlify.com/build/edge-functions/limits/" rel="noopener noreferrer"&gt;Current Netlify documentation&lt;/a&gt; still lists a 50 ms CPU budget per request, 512 MB per set of deployed Edge Functions, and a 20 MB compressed code limit. &lt;a href="https://docs.netlify.com/build/edge-functions/api/" rel="noopener noreferrer"&gt;The API documentation&lt;/a&gt; still describes npm package support as beta and notes caveats for native binaries and runtime file imports. A physical filesystem inside a microVM can remove a technical obstacle, but product support and documented limits change on their own schedule.&lt;/p&gt;

&lt;p&gt;There is a broader systems lesson in the migration. The unit to optimize is the complete request path. Isolation technology matters, but so do placement, image distribution, snapshots, cache locality, telemetry, and safe fleet rollout. Netlify made microVMs work at edge speed by changing all of those pieces together. The headline improvement is credible as a measured platform result; it should not be read as a general claim that VMs are always faster than isolates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.netlify.com/blog/edge-functions-firecracker-microvms/" rel="noopener noreferrer"&gt;Netlify engineering, &lt;em&gt;5x faster Edge Functions: v8 isolates to Firecracker MicroVMs&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unikraft.com/blog/netlify-edge-functions" rel="noopener noreferrer"&gt;Unikraft, &lt;em&gt;How Netlify Runs all of Edge Functions on Unikraft microVMs&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.netlify.com/build/edge-functions/limits/" rel="noopener noreferrer"&gt;Netlify, &lt;em&gt;Edge Functions limits&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.netlify.com/build/edge-functions/api/" rel="noopener noreferrer"&gt;Netlify, &lt;em&gt;Edge Functions API&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>GPT-6.1 Sol and Dots: A Cheaper Engine and an Agent That Keeps Working</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Wed, 30 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/gpt-61-sol-and-dots-a-cheaper-engine-and-an-agent-that-keeps-working-2j79</link>
      <guid>https://dev.to/0xgosu/gpt-61-sol-and-dots-a-cheaper-engine-and-an-agent-that-keeps-working-2j79</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fako4yuzliz45t0bq9ubn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fako4yuzliz45t0bq9ubn.webp" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenAI made two announcements on September 29 that are easy to read as one story. &lt;strong&gt;GPT-6.1 Sol&lt;/strong&gt; is a stronger, cheaper model for complex work. &lt;strong&gt;Dots&lt;/strong&gt; are always-on agents that can keep projects moving between conversations. Together, they show where OpenAI wants AI work to go: capable models underneath, persistent agents on top, and people setting the boundaries.&lt;/p&gt;

&lt;p&gt;There is an important distinction. &lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;OpenAI says dots are powered by GPT-6 Astra&lt;/a&gt;, its frontier model. It has &lt;strong&gt;not&lt;/strong&gt; said that GPT-6.1 Sol is the model running your dot. Sol and dots launched together, but they solve different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPT-6.1 Sol makes serious agent work less expensive
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openai.com/index/introducing-gpt-6-1-sol/" rel="noopener noreferrer"&gt;GPT-6.1 Sol&lt;/a&gt; upgrades GPT-6 Sol across coding, professional documents, computer use, and scientific workflows. OpenAI’s central claim is that it approaches GPT-6 Astra on several agentic tasks while costing one-fifth as much for standard input and output tokens.&lt;/p&gt;

&lt;p&gt;The reported gains are specific enough to matter, though they are still launch evaluations rather than a guarantee for every workload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;strong&gt;DeepSWE 1.1&lt;/strong&gt; , Sol matches Astra on long-running software engineering tasks at roughly one-fifth the cost per task. It beats GPT-6 Sol’s best score by 6.4 percentage points at a lower reasoning setting.&lt;/li&gt;
&lt;li&gt;On &lt;strong&gt;OSWorld 2.0’s offline set&lt;/strong&gt; , it improves on GPT-6 Sol by seven percentage points at maximum reasoning effort and lands within 2.1 points of Astra, at roughly one-seventh Astra’s cost per task.&lt;/li&gt;
&lt;li&gt;On a set of difficult, previously error-flagged conversations, the share of answers containing a factual error falls from &lt;strong&gt;11.4% to 7.7%&lt;/strong&gt; at low reasoning effort. OpenAI says this set is deliberately hard and is not representative of typical use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, the economics may be the largest change. Standard API pricing is &lt;strong&gt;$2 per million input tokens, $0.10 per million cached input tokens, and $10 per million output tokens&lt;/strong&gt;. That cached-input price is half GPT-6 Sol’s. It rewards agent workflows that repeatedly use the same instructions, tools, and project context. &lt;a href="https://developers.openai.com/api/docs/models/gpt-6.1-sol" rel="noopener noreferrer"&gt;The model page&lt;/a&gt; lists a 1,050,000-token context window and notes that longer prompts have different pricing.&lt;/p&gt;

&lt;p&gt;The practical test is cost &lt;strong&gt;per completed task&lt;/strong&gt; , not cost per token alone. An agent that needs fewer retries, makes fewer mistaken tool calls, or finishes a task at a lower reasoning setting can be cheaper even before the token discount. OpenAI’s benchmark comparisons point in that direction; teams should check the effect on their own tasks before changing defaults.&lt;/p&gt;

&lt;p&gt;GPT-6.1 Sol is available in &lt;strong&gt;Codex and ChatGPT Work&lt;/strong&gt; for Plus, Pro, Business, Enterprise, and Edu users, and through the API as &lt;code&gt;gpt-6.1-sol&lt;/code&gt;. It is &lt;strong&gt;not yet available in Chat&lt;/strong&gt;. OpenAI also says an Ultrafast option is coming to Codex in the days after launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dots make an agent a continuing collaborator
&lt;/h2&gt;

&lt;p&gt;A normal chat starts when you ask and pauses when the answer arrives. &lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;Dots&lt;/a&gt; are designed to maintain responsibility for work over time. A dot has its own cloud computer and browser, can use connected apps, and can pursue several projects without making you manage a separate conversation for every step. You can inspect its computer, redirect its work, and give feedback so it learns your preferences.&lt;/p&gt;

&lt;p&gt;OpenAI’s examples make the intended use concrete. A developer’s dot can notice patterns in customer feedback, prepare and test small fixes, and bring back pull requests for review. A researcher’s dot can rerun an analysis when new data arrives and update the figures and explanation. One early tester’s dot noticed a missing invoice, prepared it, and sent it &lt;strong&gt;after approval&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dots can be reached in ChatGPT, Slack, and Teams; OpenAI says texting is coming later. They can also hand work into Codex or ChatGPT Work. This is where the two launches meet at the product level: a dot can coordinate ongoing work, while Codex and Work provide places to execute tasks. OpenAI has not specified that those delegated tasks always use Sol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autonomy needs visible limits
&lt;/h2&gt;

&lt;p&gt;The difference between a helpful background agent and an unwelcome one is control over what it may see and do. OpenAI describes several boundaries for dots:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App access is chosen by the user.&lt;/strong&gt; A dot’s cloud computer is separate from your own computer unless you explicitly connect yours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proactive research is read-only.&lt;/strong&gt; When a dot looks for useful work in the background, those app tools cannot send messages or change app content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions have review rules.&lt;/strong&gt; Built-in rules and user-defined Custom Rules decide what can proceed, what needs approval, and what is blocked. An auto-review system checks actions that may affect accounts or share information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work stays inspectable.&lt;/strong&gt; Activity View shows progress, and OpenAI says safety monitoring can pause or stop concerning behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These controls matter because persistent access magnifies small mistakes. A model that misunderstands a request once may produce one bad answer; an agent that keeps acting on the misunderstanding can produce a chain of them. OpenAI explicitly advises reviewing consequential work. The useful measure for dots will be how often they bring back correct, reviewable results without taking actions their owners did not intend.&lt;/p&gt;

&lt;p&gt;Dots are &lt;strong&gt;rolling out&lt;/strong&gt; , starting with a primary dot for eligible Pro and Business Premium users; Enterprise workspaces, including Edu and Healthcare, can try a beta when an admin enables it. OpenAI says the first dot is included in Pro or Business Premium at no extra cost, with an allowance for deeper work. It is also piloting &lt;strong&gt;specialist dots&lt;/strong&gt; that organizations provision with their own identities and access for defined responsibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these launches mean together
&lt;/h2&gt;

&lt;p&gt;GPT-6.1 Sol makes capable tool-using work easier to justify economically. Dots change how that work is requested and supervised: instead of reopening a chat and restating the goal, you can give an agent a continuing responsibility and review what it brings back.&lt;/p&gt;

&lt;p&gt;The launches also expose two separate questions. &lt;strong&gt;Can the model finish the task well enough for the price?&lt;/strong&gt; Sol’s early results make it a strong candidate for coding, document-heavy work, and computer use. &lt;strong&gt;Can the agent stay useful and within its permissions over time?&lt;/strong&gt; Dots will have to answer that through real-world reliability, clear activity records, and approvals that match the stakes of each action.&lt;/p&gt;

&lt;p&gt;My starting point would be a bounded responsibility with an obvious review step: triage feedback and draft fixes, prepare a recurring brief, or keep an analysis current. That is enough to test whether a dot saves attention without handing it vague authority. For developers building their own agents, GPT-6.1 Sol is worth evaluating on the same kind of full task, including retries and review time, rather than on a single prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-gpt-6-1-sol/" rel="noopener noreferrer"&gt;Introducing GPT-6.1 Sol — OpenAI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-dots/" rel="noopener noreferrer"&gt;Introducing dots — OpenAI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.openai.com/api/docs/models/gpt-6.1-sol" rel="noopener noreferrer"&gt;GPT-6.1 Sol model details — OpenAI Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>Cloudflare cf: One CLI for an Entire API</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/cloudflare-cf-one-cli-for-an-entire-api-177l</link>
      <guid>https://dev.to/0xgosu/cloudflare-cf-one-cli-for-an-entire-api-177l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbnq1vv3a2ovbtbf1anj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbnq1vv3a2ovbtbf1anj8.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine asking an agent to deploy a Worker, inspect its logs, attach a database, protect the route with Access, and configure a WAF rule. The agent can write the application, but it still needs a dependable way to find and invoke five different parts of the platform. A tool with a few polished deployment commands leaves the rest of that work to API calls, browser automation, or guesswork.&lt;/p&gt;

&lt;p&gt;Cloudflare’s new &lt;a href="https://blog.cloudflare.com/cloudflare-cf-cli-launch/" rel="noopener noreferrer"&gt;&lt;code&gt;cf&lt;/code&gt; CLI&lt;/a&gt; attacks that mismatch at the interface. It is an open beta that exposes more than 3,000 Cloudflare API operations as commands, while Wrangler has accumulated roughly 280 command paths. Cloudflare reports that agents accounted for 48% of Wrangler usage in the week before the announcement, up from 25% in March 2026. The design starts from a practical observation: an agent needs discoverable operations and predictable data more than it needs a hand-crafted terminal table for every product.&lt;/p&gt;

&lt;p&gt;That shift affects more than command count. &lt;code&gt;cf&lt;/code&gt; also introduces command search, JSON-first output, a TypeScript configuration file for Workers, and Vite as the default development path. Together, these changes form a new workflow from intent to API call to reviewed configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a broad CLI is hard
&lt;/h2&gt;

&lt;p&gt;Wrangler grew around Workers. As Cloudflare added products, teams added their own commands and vocabulary. The same kind of lookup could become &lt;code&gt;d1 info&lt;/code&gt;, &lt;code&gt;hyperdrive get&lt;/code&gt;, or &lt;code&gt;workflows describe&lt;/code&gt;. Each command can make sense in isolation, yet an automated client must learn all the differences. There is also a coverage ceiling: maintaining bespoke code for thousands of API operations is expensive, especially when many are rarely called from a terminal.&lt;/p&gt;

&lt;p&gt;Cloudflare’s answer is to generate command coverage from the API descriptions it already maintains. Its &lt;a href="https://blog.cloudflare.com/forge-open-source-generation-pipeline/" rel="noopener noreferrer"&gt;Forge pipeline&lt;/a&gt; uses annotated OpenAPI schemas to generate SDKs, documentation, and now CLI commands from a common source. Product-specific metadata can improve a command without requiring each team to build a separate command framework.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fcloudflare-cf-cli%2Fapi-to-command.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fcloudflare-cf-cli%2Fapi-to-command.svg%25E2%2580%259D" alt="“An" width="800" height="400"&gt;&lt;/a&gt;Generated coverage connects the API catalog to a consistent command surface.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Generation solves coverage, but it does not by itself solve discovery. An agent cannot paste 3,000 command descriptions into every prompt. &lt;code&gt;cf cli search&lt;/code&gt; searches a small index of API descriptions and parameters using a natural-language request, then returns likely commands. The CLI points agents toward this search when they first run &lt;code&gt;--help&lt;/code&gt;. The useful pattern is narrow: search for the operation, inspect its help and required arguments, then execute it. A guessed command name is a poor substitute for that sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON is the contract between commands
&lt;/h2&gt;

&lt;p&gt;Terminal tables help humans scan a few rows. They are awkward for programs: columns move, values truncate, and decorative characters complicate parsing. Wrangler has &lt;code&gt;--json&lt;/code&gt; on some commands, but the flag is not universal. &lt;code&gt;cf&lt;/code&gt; makes JSON the default response format, pretty-printed for a person and condensed for an agent.&lt;/p&gt;

&lt;p&gt;That changes how a multi-step operation can be composed. An agent can list resources, select a specific ID, pass it to another command, and retain the exact result as evidence. It can filter fields with &lt;code&gt;jq&lt;/code&gt; without scraping a table. Human-readable forms still matter for operations that need personal judgment, such as choosing a domain. Cloudflare says &lt;code&gt;cf&lt;/code&gt; can turn a complex API request into a validated form so a person can supply those inputs.&lt;/p&gt;

&lt;p&gt;The key design choice is consistency. A command surface only becomes dependable automation infrastructure when success, failure, IDs, and parameter shapes can be handled predictably across products. JSON does not remove the need to check permissions or confirm a destructive operation. It makes the result inspectable before the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worker configuration becomes code with types
&lt;/h2&gt;

&lt;p&gt;The second half of the launch is &lt;code&gt;cloudflare.config.ts&lt;/code&gt;. It starts with Workers, although Cloudflare intends to expand it to other products later. A TypeScript file can expose a schema to an editor and language server, so an agent editing a binding gets completion and type feedback while it works. Wrangler’s TOML and JSONC formats could be validated, but they were less natural for programmatic reuse.&lt;/p&gt;

&lt;p&gt;The configuration uses &lt;code&gt;defineConfig&lt;/code&gt; from &lt;code&gt;cf/config&lt;/code&gt;. A function can choose values from Vite’s &lt;code&gt;mode&lt;/code&gt;, allowing development and production environments to share one base instead of copying large &lt;code&gt;env&lt;/code&gt; blocks. Cloudflare reports that some internal configuration files shrank by 40% from more than 5,000 lines after applying this pattern. That is an internal example, not a promise that every project will shrink by the same amount.&lt;/p&gt;

&lt;p&gt;Bindings are also declared through typed helpers. &lt;code&gt;bindings.text&lt;/code&gt;, &lt;code&gt;bindings.secret&lt;/code&gt;, &lt;code&gt;bindings.kv&lt;/code&gt;, &lt;code&gt;bindings.d1&lt;/code&gt;, &lt;code&gt;bindings.r2&lt;/code&gt;, and &lt;code&gt;bindings.queue&lt;/code&gt; express what a Worker expects in one discoverable place. A &lt;code&gt;triggers&lt;/code&gt; helper brings fetch routes, schedules, queues, and email triggers into one list. This is useful to a human reviewer as well as an agent: the code describes the runtime dependencies and entry points near the application.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fcloudflare-cf-cli%2Fconfig-runtime.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fcloudflare-cf-cli%2Fconfig-runtime.svg%25E2%2580%259D" alt="“A" width="800" height="400"&gt;&lt;/a&gt;The config file becomes a checked description of the Worker and its platform connections.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;There is a boundary to keep in mind. A typed configuration can catch misspelled fields and guide edits; it cannot prove that an API token has the intended scope, that a live resource exists, or that a production route is safe to change. Review and deployment checks still need to cover those runtime facts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vite changes the local path
&lt;/h2&gt;

&lt;p&gt;Wrangler built its own development and bundling experience before Vite existed. &lt;code&gt;cf&lt;/code&gt; uses Vite by default for JavaScript Workers, including the Cloudflare Vite plugin. That brings the Worker into Vite’s development server, hot module replacement, plugin ecosystem, and build pipeline. Cloudflare recommends the Vite plugin alongside its Vitest plugin for local testing with Workers bindings and runtime APIs.&lt;/p&gt;

&lt;p&gt;This is not an instant forced migration for every Worker. Some projects rely on Wrangler’s esbuild behavior, and Rust or Python Workers have different build requirements. During the beta, &lt;code&gt;cf&lt;/code&gt; delegates development and deployment for those cases back to Wrangler. The &lt;code&gt;cf migrate&lt;/code&gt; command converts existing Vite-based Workers to &lt;code&gt;cloudflare.config.ts&lt;/code&gt;; other projects can adopt the CLI while retaining their current build path. Cloudflare says it plans one final major Wrangler release after the beta and 18 months of maintenance support after that point. That is a stated plan, so teams should check current migration guidance before scheduling a change.&lt;/p&gt;

&lt;p&gt;For a new project, the announcement gives a short route: install with &lt;code&gt;npm i -g cf&lt;/code&gt;, use &lt;code&gt;cf init&lt;/code&gt; for a starter Worker, or run &lt;code&gt;cf init/deploy&lt;/code&gt; to configure an existing project. A static site can start with &lt;code&gt;cf deploy&lt;/code&gt; without a config file. The &lt;a href="https://github.com/cloudflare/cf" rel="noopener noreferrer"&gt;open-source repository&lt;/a&gt; is the place to check current commands and report beta issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safe way to use it with an agent
&lt;/h2&gt;

&lt;p&gt;The biggest opportunity is a workflow that discovers instead of memorizes. Give the agent a bounded goal and permission to inspect. It can search for commands, read their argument help, inspect existing resources, and propose the intended change. For a write, the important checkpoint is the exact account, zone, Worker, or resource ID and the parameters to be sent. After execution, structured output can confirm what changed.&lt;/p&gt;

&lt;p&gt;That procedure matters because the new coverage includes high-impact operations. Cloudflare’s launch example spans deployment, observability, Access, domains, and WAF. A single CLI makes the path shorter; it also makes account selection, credential scope, and review of write operations more consequential. The practical rule is to use command discovery for breadth and explicit checks for authority.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;cf&lt;/code&gt; launch is therefore a useful case study in agent-facing developer tools. The large API surface is generated from a common source, commands are searchable on demand, outputs are structured by default, and configuration can be checked in the editor. Those pieces let an agent work across products without carrying the whole manual in context. The quality of the result still depends on selecting the right operation and verifying its effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/cloudflare-cf-cli-launch/" rel="noopener noreferrer"&gt;Cloudflare’s &lt;code&gt;cf&lt;/code&gt; announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/forge-open-source-generation-pipeline/" rel="noopener noreferrer"&gt;Cloudflare Forge generation pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cloudflare/cf" rel="noopener noreferrer"&gt;Cloudflare &lt;code&gt;cf&lt;/code&gt; source repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/changelog/post/2026-09-08-miniflare-v5/" rel="noopener noreferrer"&gt;Miniflare v5 and the new configuration shape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/agent-setup/codex/" rel="noopener noreferrer"&gt;Cloudflare agent setup guidance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>Claude Sonnet 5.5: Near-Opus Results at Half the Token Price</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/claude-sonnet-55-near-opus-results-at-half-the-token-price-539i</link>
      <guid>https://dev.to/0xgosu/claude-sonnet-55-near-opus-results-at-half-the-token-price-539i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F0xgosu.dev%2Fblog%2Fclaude-sonnet-5-5%2Fhero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F0xgosu.dev%2Fblog%2Fclaude-sonnet-5-5%2Fhero.svg" width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anthropic released &lt;strong&gt;Claude Sonnet 5.5&lt;/strong&gt; on September 28, 2026. It is the second model in the Claude 5.5 family, six days after &lt;a href="https://0xgosu.dev/blog/claude-opus-5-5-efficient-frontier-agents/" rel="noopener noreferrer"&gt;Claude Opus 5.5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The headline is simple: on most of Anthropic’s published benchmarks, Sonnet 5.5 lands within a few points of Opus 5.5, while costing &lt;strong&gt;half as much per input and output token&lt;/strong&gt;. It also generates output more than 30% faster than &lt;a href="https://0xgosu.dev/blog/claude-sonnet-5-agentic-ai-model/" rel="noopener noreferrer"&gt;Claude Sonnet 5&lt;/a&gt; and, according to Anthropic, costs up to 30% less per task because it needs fewer tokens to finish the same work.&lt;/p&gt;

&lt;p&gt;That makes Sonnet 5.5 less of a “cheaper fallback” and more of a real default. The question for most teams is no longer “is Sonnet good enough?” but “which work still needs Opus?”&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Big jump over Sonnet 5&lt;/strong&gt; in coding, knowledge work, computer use, and chart reading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close to Opus 5.5&lt;/strong&gt; on most benchmarks, sometimes within one or two points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half the token price of Opus 5.5&lt;/strong&gt; for input, output, and cache writes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30%+ faster output&lt;/strong&gt; than Sonnet 5, and fewer tokens per task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Sonnet model with cyber safeguards&lt;/strong&gt; , which visibly fall back to Sonnet 5 for high-risk security requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One migration change&lt;/strong&gt; if you run Sonnet with thinking turned off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic is also clear about the limit: Opus 5.5 “remains clearly stronger at complex, open-ended work requiring sustained judgment.” Sonnet 5.5 is built for well-scoped everyday work: fixing bugs, iterating fast, and producing documents, slides, and spreadsheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Sonnet 5.5 fits
&lt;/h2&gt;

&lt;p&gt;The Claude 5.5 family now looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Opus 5.5&lt;/strong&gt; — complex work that needs careful judgment over long sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sonnet 5.5&lt;/strong&gt; — well-scoped everyday tasks, fast iteration, polished documents and UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Haiku 5.5&lt;/strong&gt; — high-volume, cost-sensitive work, arriving in the coming weeks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The split is not really about intelligence tiers anymore. On many tasks, the two larger models score about the same. The split is about &lt;strong&gt;how much open-ended judgment a task needs&lt;/strong&gt; and &lt;strong&gt;how much you want to spend per attempt&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark picture
&lt;/h2&gt;

&lt;p&gt;These are Anthropic’s reported results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation&lt;/th&gt;
&lt;th&gt;Sonnet 5.5&lt;/th&gt;
&lt;th&gt;Sonnet 5&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;GPT-6 Sol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terminal-Bench 4.0&lt;/td&gt;
&lt;td&gt;70.6%&lt;/td&gt;
&lt;td&gt;10.3%&lt;/td&gt;
&lt;td&gt;66.4%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FrontierCode 1.1 (Main)&lt;/td&gt;
&lt;td&gt;52.1% (Xhigh) / 46.2% (Max)&lt;/td&gt;
&lt;td&gt;42.4%&lt;/td&gt;
&lt;td&gt;54.4%&lt;/td&gt;
&lt;td&gt;49.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CursorBench 4.0&lt;/td&gt;
&lt;td&gt;55.5%&lt;/td&gt;
&lt;td&gt;34.1%&lt;/td&gt;
&lt;td&gt;57.8%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GDPval-AA v2.1&lt;/td&gt;
&lt;td&gt;1844&lt;/td&gt;
&lt;td&gt;1449&lt;/td&gt;
&lt;td&gt;1846&lt;/td&gt;
&lt;td&gt;1487&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AA-Briefcase v1.1&lt;/td&gt;
&lt;td&gt;1811&lt;/td&gt;
&lt;td&gt;1359&lt;/td&gt;
&lt;td&gt;1822&lt;/td&gt;
&lt;td&gt;1483&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Humanity’s Last Exam (tools)&lt;/td&gt;
&lt;td&gt;64.5%&lt;/td&gt;
&lt;td&gt;54.9%&lt;/td&gt;
&lt;td&gt;67.7%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld 2.1&lt;/td&gt;
&lt;td&gt;80.1%&lt;/td&gt;
&lt;td&gt;57.0%&lt;/td&gt;
&lt;td&gt;81.8%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chartography (no tools)&lt;/td&gt;
&lt;td&gt;61.6%&lt;/td&gt;
&lt;td&gt;15.6%&lt;/td&gt;
&lt;td&gt;64.4%&lt;/td&gt;
&lt;td&gt;53.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few details matter more than the headline numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal-Bench is the one place Sonnet 5.5 beats Opus 5.5&lt;/strong&gt; , but read it carefully. Anthropic’s footnote says the Opus 5.5 score is at Xhigh effort, its best result. The jump from Sonnet 5’s 10.3% is so large that it deserves independent confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FrontierCode is lower at Max effort than at Xhigh.&lt;/strong&gt; At Max, Sonnet 5.5 more often ran Claude Code’s multi-agent code-review skill. That sometimes caused timeouts or extra edits outside the task’s scope, which this benchmark penalizes. More effort is not always better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The knowledge-work numbers come with caveats.&lt;/strong&gt; Artificial Analysis ran GDPval-AA and AA-Briefcase on a pre-release build that had a structured-output bug (since fixed). The GPT-6 Sol scores may also predate an OpenAI fix for image understanding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GDPval-AA is nearly a tie.&lt;/strong&gt; Sonnet 5.5 scores 1844 against Opus 5.5’s 1846, and about 400 points above Sonnet 5.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is consistent: Sonnet 5.5 closes most of the gap to Opus 5.5 and pulls far ahead of Sonnet 5. The biggest jumps are in chart reading (+46 points), terminal coding, and computer use (+23 points on OSWorld 2.1).&lt;/p&gt;

&lt;h2&gt;
  
  
  Price per token vs. cost per task
&lt;/h2&gt;

&lt;p&gt;Anthropic says Sonnet 5.5 is priced the same as Sonnet 5. Against Opus 5.5, the comparison is clean:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Price per 1M tokens&lt;/th&gt;
&lt;th&gt;Sonnet 5.5&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$4&lt;/td&gt;
&lt;td&gt;half&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;half&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache writes&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;half&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache reads&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The more useful number is cost per task, which depends on how many tokens and steps the model uses. Anthropic’s cost charts make a few strong claims:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On several benchmarks, Sonnet 5.5 at &lt;strong&gt;Low or Medium&lt;/strong&gt; effort beats &lt;strong&gt;Sonnet 5’s best score&lt;/strong&gt; for about a tenth of the cost per task.&lt;/li&gt;
&lt;li&gt;On FrontierCode at &lt;strong&gt;High&lt;/strong&gt; effort, it scores 10 points higher than Sonnet 5 at the same setting, for about one fifteenth of the cost.&lt;/li&gt;
&lt;li&gt;At High effort, it matches GPT-6 Sol’s best FrontierCode score for about a fifth of the cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic also notes that Sonnet 5.5 complements Opus 5.5 best at &lt;strong&gt;lower effort settings&lt;/strong&gt;. At the highest settings, the two models can reach similar scores at a similar cost. In other words, the savings come from running Sonnet 5.5 at Low or Medium effort on work that does not need more, not from running it at Max effort on everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What early testers noticed: fewer tokens, fewer steps
&lt;/h2&gt;

&lt;p&gt;Launch quotes are chosen by the vendor, so treat them as signals, not proof. Still, the same theme shows up across most of them: Sonnet 5.5 reaches a result with less work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Balyasny Asset Management:&lt;/strong&gt; about 121k tokens per answer on 2,441 finance tasks, compared with 497k for Sonnet 5, with higher scores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base44:&lt;/strong&gt; across 118 real app builds, 3.6 iterations per build on average, compared with 7.7 for Opus 5, with similar app quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable:&lt;/strong&gt; a third fewer tool calls and roughly half the shell runs to finish a coding task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack:&lt;/strong&gt; better results on almost all offline Slackbot evals, with about 14% fewer output tokens and no prompt changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Box:&lt;/strong&gt; 2.4x faster and 12% fewer total tokens, while catching data errors Sonnet 5 missed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zendesk:&lt;/strong&gt; tickets processed 20% faster, with fewer wrong decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeRabbit:&lt;/strong&gt; Sonnet 5’s habit of reaching for web search too often is gone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic’s own explanation matches this: in head-to-head runs, Sonnet 5.5 batched tool calls together more often than Sonnet 5. For agents, fewer steps means less waiting, lower cost, and fewer chances to drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge work, design, and long tasks
&lt;/h2&gt;

&lt;p&gt;Sonnet 5.5 is also pitched as a better partner for documents and design work. Anthropic says testers noticed it adds polish to user interfaces and can follow slide templates closely enough that decks need little editing.&lt;/p&gt;

&lt;p&gt;In one internal test, Anthropic gave it a public company’s earnings materials, call transcripts, and a slide template, and asked for a 10-slide operating review. Two experts judged the first draft ready to send as is.&lt;/p&gt;

&lt;p&gt;It is also stronger on long-horizon work and image understanding. It is the first Sonnet model to beat Pokémon Red using only screenshots. That is a playful test, but it checks something real: staying on track over a very long task using only visual input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety and safeguards
&lt;/h2&gt;

&lt;p&gt;Anthropic says Sonnet 5.5 does not push the frontier of its models’ capabilities, so the alignment review focused on risks that apply at any level: acting against users, misleading them, and helping with high-stakes misuse.&lt;/p&gt;

&lt;p&gt;The reported results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On an automated behavioral audit of about 1,850 scenarios, Sonnet 5.5 &lt;strong&gt;matches or improves on Sonnet 5&lt;/strong&gt; on most measures&lt;/li&gt;
&lt;li&gt;On containment tests, it comes close to Opus 5.5 in how rarely it tries to escape its sandbox, and is &lt;strong&gt;the least likely of any Claude model to probe container limits&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Opus 5.5 still scores slightly better overall&lt;/li&gt;
&lt;li&gt;Anthropic found no evidence that Sonnet 5.5 pursues goals that conflict with the user’s intention, while admitting that no set of evaluations catches every failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The safeguards matter more for day-to-day use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cybersecurity:&lt;/strong&gt; Sonnet 5.5’s cyber skills are a big step up from Sonnet 5, so it is the first Sonnet model with Opus-style cyber safeguards. Routine bug finding and fixing still work. Higher-risk security tasks &lt;strong&gt;visibly fall back to Sonnet 5&lt;/strong&gt;. An expanded Cyber Verification Program is coming for defenders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Biology:&lt;/strong&gt; Same safeguards as Sonnet 5. Some microbiology and virology requests may be flagged in error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distillation:&lt;/strong&gt; New classifiers block attempts to extract its reasoning at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build security tooling on Sonnet, test these paths directly. The model you think you are calling may not be the model that answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration notes for developers
&lt;/h2&gt;

&lt;p&gt;The upgrade is mostly a model ID change, with a few details to check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Model ID:&lt;/strong&gt; &lt;code&gt;claude-sonnet-5-5&lt;/code&gt;, available on the Claude Platform, AWS, Google Cloud, and Microsoft Azure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thinking off:&lt;/strong&gt; If you run Sonnet with thinking disabled, you need to switch to the new &lt;code&gt;between_tools&lt;/code&gt; setting before moving to Sonnet 5.5. It keeps up-front thinking off. See the migration guide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effort defaults:&lt;/strong&gt; Medium in Claude Code and the Claude apps, High on the Claude Platform. Given the cost curves above, try Medium or Low for routine work before accepting the default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preserved thinking:&lt;/strong&gt; Claude’s thinking can no longer be separated from the account that created it. Most developers will not notice. If you move conversations between accounts, including switching accounts mid-session in Claude Code, read the docs first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data retention:&lt;/strong&gt; Zero data retention is available, as with Opus 5.5 and Sonnet 5.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sonnet 5.5 or Opus 5.5?
&lt;/h2&gt;

&lt;p&gt;A simple starting rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with Sonnet 5.5&lt;/strong&gt; for bug fixes, well-defined features, code review, support flows, document and slide generation, and high-volume agent steps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalate to Opus 5.5&lt;/strong&gt; for architecture decisions, ambiguous multi-week projects, large migrations, and work where one wrong judgment is expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair them&lt;/strong&gt; for larger jobs: Opus plans and sets the structure, Sonnet implements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One early tester described that last pattern directly: when Opus 5.5 sets the architecture for a game, they would be confident letting Sonnet 5.5 implement it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would evaluate Sonnet 5.5
&lt;/h2&gt;

&lt;p&gt;Do not rely on a single prompt. Compare it against both the model you use today and Opus 5.5:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick 10 to 20 real tasks from your backlog: bug fixes, small features, reviews, and reports.&lt;/li&gt;
&lt;li&gt;Run each task on Sonnet 5, Sonnet 5.5 (Low, Medium, High), and Opus 5.5.&lt;/li&gt;
&lt;li&gt;Measure success rate, total tokens, tool calls, elapsed time, and human corrections.&lt;/li&gt;
&lt;li&gt;Check whether Sonnet 5.5 at Medium matches Sonnet 5 at its best, as Anthropic claims.&lt;/li&gt;
&lt;li&gt;Find the tasks where Sonnet 5.5 fails and Opus 5.5 succeeds. Those define your routing rule.&lt;/li&gt;
&lt;li&gt;If you run with thinking off, test the &lt;code&gt;between_tools&lt;/code&gt; migration before switching production traffic.&lt;/li&gt;
&lt;li&gt;If you handle security work, send legitimate security requests and confirm how fallbacks appear to users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is not to find the smartest model. It is to find the cheapest model that reliably finishes each type of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical takeaway
&lt;/h2&gt;

&lt;p&gt;Claude Sonnet 5.5 narrows the gap between “good enough” and “best available” to a few benchmark points, at half the token price of Opus 5.5 and with noticeably fewer tokens per task than Sonnet 5.&lt;/p&gt;

&lt;p&gt;The claims are vendor-reported and still need independent testing, especially the large Terminal-Bench jump and the long-running agent results. But the direction is clear. For most everyday engineering and knowledge work, Sonnet 5.5 is positioned as the default, with Opus 5.5 kept for the work that truly needs sustained judgment.&lt;/p&gt;

&lt;p&gt;For teams paying Opus prices on routine work, that is a strong reason to re-run the routing math this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-sonnet-5-5" rel="noopener noreferrer"&gt;Anthropic: Introducing Claude Sonnet 5.5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-sonnet-5-5-system-card" rel="noopener noreferrer"&gt;Claude Sonnet 5.5 system card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/models/sonnet-5-5/migration-guide#turn-thinking-off" rel="noopener noreferrer"&gt;Sonnet 5.5 migration guide: turning thinking off&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/build-with-claude/preserved-thinking" rel="noopener noreferrer"&gt;Preserved thinking docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://academy.claude.com/tutorials/choosing-the-right-effort-level-in-claude-code" rel="noopener noreferrer"&gt;Choosing the right effort level in Claude Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://0xgosu.dev/blog/claude-opus-5-5-efficient-frontier-agents/" rel="noopener noreferrer"&gt;Claude Opus 5.5: Frontier Agents Get Faster, Cheaper, and Easier to Trust&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>Go Concurrency Is a Lifecycle Problem</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Mon, 28 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/go-concurrency-is-a-lifecycle-problem-9n3</link>
      <guid>https://dev.to/0xgosu/go-concurrency-is-a-lifecycle-problem-9n3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd66uwcm8orrj2n0li0x9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd66uwcm8orrj2n0li0x9.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Launching a goroutine takes one word: &lt;code&gt;go&lt;/code&gt;. Making that goroutine finish, return its result, respect a canceled request, and leave shared data intact is the real work. &lt;a href="https://antonz.org/go-concurrency-distilled/" rel="noopener noreferrer"&gt;Anton Zhiyanov’s interactive concurrency mini-book&lt;/a&gt; is a useful map of the language’s tools. The design principle that connects them is ownership: every concurrent task needs a reason to start, a way to communicate, and a defined way to stop.&lt;/p&gt;

&lt;p&gt;Consider a service that reads jobs, enriches them with network calls, and writes results. If each request starts another goroutine without a limit, memory and open connections can grow with incoming traffic. If the writer stops early while a worker is sending on an unbuffered channel, that worker can remain blocked forever. If workers update a shared counter without synchronization, the result can be wrong even when the pipeline seems to complete. Concurrency becomes easier to reason about when these are separate questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goroutines run independently; completion must be explicit
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;go&lt;/code&gt; statement starts a function concurrently. The runtime schedules goroutines over operating system threads; starting one does not make the caller wait for it. When &lt;code&gt;main&lt;/code&gt; returns, the process exits even if its background work is unfinished. A &lt;a href="https://pkg.go.dev/sync#WaitGroup" rel="noopener noreferrer"&gt;&lt;code&gt;sync.WaitGroup&lt;/code&gt;&lt;/a&gt; records how many tasks must complete before the owner moves on. &lt;code&gt;Add&lt;/code&gt; increments its count, &lt;code&gt;Done&lt;/code&gt; decrements it, and &lt;code&gt;Wait&lt;/code&gt; blocks until it reaches zero. In current Go, &lt;code&gt;WaitGroup.Go&lt;/code&gt; packages the start and completion bookkeeping together; the function passed to it must not panic.&lt;/p&gt;

&lt;p&gt;Waiting is only one part of ownership. A wait group cannot stop a goroutine that is stuck on a send, and it does not carry an error or result. Those need separate paths. For a short task, the result can be a value sent on a channel. For a longer request, cancellation should be propagated into every blocking stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Channels are handoffs with precise blocking rules
&lt;/h2&gt;

&lt;p&gt;An unbuffered &lt;code&gt;chan T&lt;/code&gt; transfers a value when a sender and receiver meet. The sender waits until a receiver is ready. A buffered channel, made with &lt;code&gt;make(chan T, n)&lt;/code&gt;, lets up to &lt;code&gt;n&lt;/code&gt; values wait in a queue before sends block. A buffer smooths a small burst; it is not a substitute for a throughput limit. Once it fills, producers still need a policy for waiting, dropping, or rejecting work.&lt;/p&gt;

&lt;p&gt;The sender normally owns closing a channel. Closing means &lt;strong&gt;no more values will be sent&lt;/strong&gt; , so a receiver can use &lt;code&gt;for v := range ch&lt;/code&gt; to drain values and finish. A receive from a closed, drained channel returns the zero value and &lt;code&gt;ok == false&lt;/code&gt;; a send to a closed channel panics. There is no need to close a channel merely to release its memory. A nil channel is different again: sends and receives block indefinitely, which can be useful for disabling a &lt;code&gt;select&lt;/code&gt; case but is usually a bug when accidental.&lt;/p&gt;

&lt;p&gt;Directional types make this contract visible. A producer can return &lt;code&gt;&amp;lt;-chan Result&lt;/code&gt; so callers can only receive; a consumer can accept &lt;code&gt;chan&amp;lt;- Result&lt;/code&gt; so it can only send. The type checker then catches some ownership mistakes before runtime.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fgo-concurrency-lifecycle%2Fpipeline-ownership.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fgo-concurrency-lifecycle%2Fpipeline-ownership.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;A pipeline needs a completion path and a cancellation path at every stage.   &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Select coordinates competing events
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;select&lt;/code&gt; waits for one of several channel operations. When several cases are ready, it chooses one; code must not depend on a particular winner. A &lt;code&gt;default&lt;/code&gt; case makes it nonblocking. That can implement a best-effort send, but a busy loop around &lt;code&gt;select { default: ... }&lt;/code&gt; can waste CPU.&lt;/p&gt;

&lt;p&gt;In a merge stage, &lt;code&gt;select&lt;/code&gt; can take from either upstream channel. When one closes, setting its local channel variable to &lt;code&gt;nil&lt;/code&gt; disables that case. In a worker, a &lt;code&gt;select&lt;/code&gt; between &lt;code&gt;out &amp;lt;- result&lt;/code&gt; and &lt;code&gt;&amp;lt;-ctx.Done()&lt;/code&gt; lets the worker abandon a send when the caller leaves. Without the cancellation case, a downstream reader that exits early can strand the worker. The &lt;a href="https://go.dev/blog/pipelines" rel="noopener noreferrer"&gt;Go team’s pipeline guide&lt;/a&gt; treats this as a first-class part of the pipeline, not a cleanup afterthought.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helper protects only the output send. The worker must also use context-aware network calls and check cancellation during long loops. A timeout wrapper that returns while an arbitrary &lt;code&gt;fn()&lt;/code&gt; keeps running has limited the caller’s wait, &lt;strong&gt;not&lt;/strong&gt; stopped the work. &lt;code&gt;context.WithTimeout&lt;/code&gt; or &lt;code&gt;WithDeadline&lt;/code&gt; supplies a cancellation signal; work must cooperate with it. Call the returned cancel function to release associated resources. A child context cannot extend a parent’s deadline. &lt;code&gt;context.Cause&lt;/code&gt; can preserve a more useful reason for cancellation, while ordinary function arguments remain a clearer home for application data than &lt;code&gt;context.WithValue&lt;/code&gt; in most cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipelines need an error policy
&lt;/h2&gt;

&lt;p&gt;A pipeline connects a source, one or more processing stages, and a sink. Each stage receives from an input channel and sends to an output channel. The simplest stage closes its own output when its work ends. Once multiple workers share an output, a coordinator must wait for &lt;em&gt;all&lt;/em&gt; senders before closing it. Closing from one worker can panic another worker that is still sending.&lt;/p&gt;

&lt;p&gt;There are several valid error contracts. A fail-fast pipeline cancels the group at the first error and returns that error. A best-effort pipeline sends a &lt;code&gt;Result{Value, Err}&lt;/code&gt; for each item, allowing the caller to decide which failures to tolerate. A separate error channel can work, but it introduces another stream that must be drained and closed correctly. Choose the contract before adding goroutines; otherwise an error can leave a producer blocked behind a consumer that has already returned.&lt;/p&gt;

&lt;p&gt;Timers are another source of events. &lt;code&gt;time.After&lt;/code&gt; suits a one-off wait, &lt;code&gt;time.NewTimer&lt;/code&gt; can be stopped or reset, and &lt;code&gt;time.NewTicker&lt;/code&gt; handles periodic work. A ticker may skip ticks if its reader is slow. Whichever timer you use, make the task’s exit condition explicit; an endless ticker loop does not stop just because its caller no longer cares.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bound work before it grows
&lt;/h2&gt;

&lt;p&gt;A buffered channel can act as a semaphore: a task acquires a slot by sending an empty value and releases it by receiving one. Put acquisition &lt;strong&gt;before&lt;/strong&gt; spawning a goroutine if the goal is to bound both active work and goroutine count. If every incoming item first starts a goroutine and only then waits for a slot, queued goroutines can still grow without limit. &lt;a href="https://go.dev/doc/effective_go#channels" rel="noopener noreferrer"&gt;Effective Go&lt;/a&gt; calls out exactly this resource-boundary issue. A fixed worker pool is another clear option. More complex weighted limits can use &lt;a href="https://pkg.go.dev/golang.org/x/sync/semaphore" rel="noopener noreferrer"&gt;&lt;code&gt;golang.org/x/sync/semaphore&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The capacity is a service decision, not automatically the CPU count. Network workers might need more concurrency than available cores, while a downstream API might tolerate far fewer simultaneous calls. Measure throughput, queue depth, tail latency, and error rates as the limit changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared state needs its own rule
&lt;/h2&gt;

&lt;p&gt;Channels are well suited to transferring ownership of work. A &lt;code&gt;sync.Mutex&lt;/code&gt; is often simpler for a small shared map or counter. Guard every read and write that can overlap. An &lt;code&gt;RWMutex&lt;/code&gt; allows concurrent readers but still gives writers exclusive access; it is useful only when its extra complexity pays off. &lt;code&gt;TryLock&lt;/code&gt; is a deliberate refusal to wait, not a general fix for contention. The Go wiki’s &lt;a href="https://go.dev/wiki/MutexOrChannel" rel="noopener noreferrer"&gt;mutex-or-channel guide&lt;/a&gt; recommends choosing the primitive that makes the code clearest.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;data race&lt;/strong&gt; occurs when concurrent operations access the same memory, at least one writes, and there is no synchronization. &lt;code&gt;go test -race ./...&lt;/code&gt; can catch races on executed paths. A &lt;strong&gt;race condition&lt;/strong&gt; is broader: the program can be memory-safe yet produce the wrong result because events happen in an unexpected order. For example, a mutex can protect each bank balance read and write while a multi-step transfer still violates a business invariant if the whole operation is not coordinated. The &lt;a href="https://go.dev/doc/articles/race_detector" rel="noopener noreferrer"&gt;race detector’s documentation&lt;/a&gt; is explicit that it sees only paths exercised at runtime.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fgo-concurrency-lifecycle%2Fsynchronization-choice.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fgo-concurrency-lifecycle%2Fsynchronization-choice.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Pick synchronization based on the relationship between tasks, not on the novelty of the primitive.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Some specialized tools complete the picture. &lt;code&gt;sync/atomic&lt;/code&gt; offers operations such as &lt;code&gt;Load&lt;/code&gt;, &lt;code&gt;Store&lt;/code&gt;, and &lt;code&gt;CompareAndSwap&lt;/code&gt; for narrowly scoped shared values, but a collection of individually atomic fields is not automatically one atomic transaction. &lt;code&gt;sync.Once&lt;/code&gt; runs one initializer once; &lt;code&gt;OnceFunc&lt;/code&gt;, &lt;code&gt;OnceValue&lt;/code&gt;, and &lt;code&gt;OnceValues&lt;/code&gt; package common one-time patterns. A &lt;code&gt;sync.Cond&lt;/code&gt; lets waiters sleep until shared state changes; always recheck its condition in a loop after waking. Closing a channel can broadcast a one-time signal to many receivers. &lt;code&gt;sync.Pool&lt;/code&gt; can reuse temporary objects, but pooled items may disappear during garbage collection, so it is neither durable storage nor a concurrency limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test schedules, not sleeps
&lt;/h2&gt;

&lt;p&gt;Concurrency bugs often hide because one test run happened to choose a friendly schedule. First test the observable contract: all expected results arrive, early cancellation releases workers, output channels close only after senders finish, and error paths return. Run the race detector under representative workloads. For time-dependent code, &lt;a href="https://pkg.go.dev/testing/synctest" rel="noopener noreferrer"&gt;&lt;code&gt;testing/synctest&lt;/code&gt;&lt;/a&gt; runs a group of goroutines with a fake clock and can wait until the group is durably blocked. This makes many timeout tests fast and repeatable. Its bubble is not a model of arbitrary network I/O or external processes, so keep those dependencies fake or outside the test.&lt;/p&gt;

&lt;p&gt;When a service hangs or slows, collect evidence before adjusting channel sizes. Goroutine profiles show where tasks are blocked; mutex and block profiles expose contention; execution traces reveal scheduling and waiting over time. The Go runtime can run many goroutines over fewer OS threads, but concurrency does not guarantee parallel speedup. CPU-bound work still needs enough available processors, and extra goroutines can add overhead. A useful diagnostic question is: &lt;strong&gt;which owner is expected to make this blocked operation progress, and can that owner still run?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The practical design rule is small: assign one owner to every channel closure, give each worker a completion signal, make cancellation reachable from every blocking operation, and put limits at the point where work enters. Those choices turn Go’s concurrency primitives into a system whose behavior can be explained when the easy path ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://antonz.org/go-concurrency-distilled/" rel="noopener noreferrer"&gt;Anton Zhiyanov, &lt;em&gt;Go Concurrency Distilled&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.dev/blog/pipelines" rel="noopener noreferrer"&gt;The Go team, &lt;em&gt;Go Concurrency Patterns: Pipelines and Cancellation&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/effective_go#concurrency" rel="noopener noreferrer"&gt;Effective Go: Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/articles/race_detector" rel="noopener noreferrer"&gt;Go Data Race Detector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/testing/synctest" rel="noopener noreferrer"&gt;Go &lt;code&gt;testing/synctest&lt;/code&gt; package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>A Local Language Model Can Make Decisions Without Writing Answers</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/a-local-language-model-can-make-decisions-without-writing-answers-1cin</link>
      <guid>https://dev.to/0xgosu/a-local-language-model-can-make-decisions-without-writing-answers-1cin</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4a9dmwcqmma2t4l8twlw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4a9dmwcqmma2t4l8twlw.png" width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A language model does not have to write a sentence to be useful. Sometimes an application already knows the possible actions. It needs to decide which one fits the current state, and perhaps how much uncertainty to attach to each option.&lt;/p&gt;

&lt;p&gt;That is the idea behind &lt;a href="https://www.nobodywho.ai/posts/jev-in-25-lines/" rel="noopener noreferrer"&gt;NobodyWho’s small Python demonstration&lt;/a&gt;. It feeds a local Qwen model an email and three labels—legitimate, spam, and phishing—then reads the model’s next-token scores instead of asking it to generate a reply. The example is deliberately provocative: its author presents it as a compact answer to &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;TypeSafe’s Jev&lt;/a&gt;, a specialized service for structured, probabilistic decisions. It is also explicitly a parody. The interesting technical question is what this short program actually demonstrates, and what still needs to be built around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The task is a choice, not a conversation
&lt;/h2&gt;

&lt;p&gt;Imagine a message claiming to be from payroll and directing an employee to enter a password on an unfamiliar sign-in page. A conversational assistant might explain the danger in several sentences. A mail filter needs an output that code can use: &lt;code&gt;Legitimate&lt;/code&gt;, &lt;code&gt;Spam&lt;/code&gt;, or &lt;code&gt;Phishing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The local demonstration loads a quantized &lt;a href="https://huggingface.co/Qwen/Qwen3-0.6B-GGUF" rel="noopener noreferrer"&gt;Qwen3 0.6B GGUF model&lt;/a&gt; with &lt;code&gt;llama-cpp-python&lt;/code&gt;. Its prompt supplies the email and the three possible answers, represented by &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, and &lt;code&gt;C&lt;/code&gt;. It ends immediately before the assistant’s answer. The model performs a prompt evaluation, and the program inspects the scores for those three label tokens at that position.&lt;/p&gt;

&lt;p&gt;That distinction matters. A normal generation loop repeatedly selects a token, appends it to the context, and runs the model again. Here the application needs only the first decision position. It can read a distribution without waiting for an explanation or JSON object to be spelled out token by token.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fjev-probability-readout%2Fdecision-readout.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fjev-probability-readout%2Fdecision-readout.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Only the scores of allowed answer tokens are read. No answer text has to be generated.   &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the 25-line idea works
&lt;/h2&gt;

&lt;p&gt;The original program has three essential stages. First it loads the model with &lt;code&gt;Llama.from_pretrained&lt;/code&gt;, selecting the GGUF file and a 512-token context. It enables &lt;code&gt;logits_all=True&lt;/code&gt;, because the example reads the wrapper’s stored score array after evaluating the prompt. The &lt;a href="https://llama-cpp-python.readthedocs.io/en/latest/api-reference/" rel="noopener noreferrer"&gt;binding’s API reference&lt;/a&gt; documents how prompt evaluation and score storage work. The exact Python interface can change, so a production implementation should pin and test its dependency version.&lt;/p&gt;

&lt;p&gt;Second, it formats an instruction and an email alongside three labeled answers. The assistant prefix includes an empty Qwen thinking block, steering the model to answer at the next token rather than begin an extended reasoning trace. The code tokenizes that prompt with special tokens enabled and passes the token IDs to &lt;code&gt;model.eval(...)&lt;/code&gt;. This is a model-specific prompt format; another model needs its own chat template.&lt;/p&gt;

&lt;p&gt;Third, it takes the last row of logits and selects only the token IDs for &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, and &lt;code&gt;C&lt;/code&gt;. A logit is an unnormalized score. It has no useful probability interpretation on its own. The program subtracts a log-sum-exp over those three scores and exponentiates the results. Mathematically, for a permitted choice, &lt;code&gt;p_i = exp(z_i) / (exp(z_A) + exp(z_B) + exp(z_C))&lt;/code&gt;, where &lt;code&gt;z_i&lt;/code&gt; is the model’s next-token logit for that label. Using log-sum-exp avoids numerical overflow when scores are large. In the article’s sample run, the resulting values are about 3.1% legitimate, 8.4% spam, and 88.5% phishing. These are an illustration from one prompt and one model run, not a measured reliability guarantee.&lt;/p&gt;

&lt;p&gt;A minimal version of the readout, once a model and prompt are prepared, looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;add_bos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;special&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;last_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_tokens&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;add_bos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;last_scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;token_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;probabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;max&lt;/code&gt; subtraction is another stable form of softmax. In real code, check that every label is exactly one token &lt;em&gt;in the answer context&lt;/em&gt;, that the three IDs differ, and that the prompt fits the context window. Otherwise a “choice score” could actually score only the first token of a longer label. Also reset or manage model state between independent requests; the demonstration is a one-shot example, not a request-serving design.&lt;/p&gt;

&lt;h2&gt;
  
  
  What those probabilities mean
&lt;/h2&gt;

&lt;p&gt;The three numbers add to one because the code renormalizes &lt;strong&gt;only across the three displayed labels&lt;/strong&gt;. That does not mean the model assigned all its probability mass to those labels. It might have preferred a newline, a refusal, or a different answer token. The code discards that outside mass and asks, “Assuming the answer is one of these three labels, how are they ranked?”&lt;/p&gt;

&lt;p&gt;Nor does 88.5% automatically mean that 88.5% of messages assigned that score will truly be phishing. That latter statement is &lt;strong&gt;calibration&lt;/strong&gt;. It requires a representative labeled test set and a comparison of predicted confidence against observed correctness. &lt;a href="https://openjev.com/" rel="noopener noreferrer"&gt;SemIf’s browser experiment&lt;/a&gt;, which compares direct readout with generated JSON on the same local model, makes this limit explicit: its direct scores are conditional choice probabilities, not calibrated confidence.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fjev-probability-readout%2Fprobability-boundaries.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fjev-probability-readout%2Fprobability-boundaries.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Normalization is an arithmetic step. Calibration is a claim about performance on real cases.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This distinction changes how a system should use the result. Suppose phishing has the highest score. A mail client might show a warning, but automatically deleting the message would need a much stronger evidence base. A responsible deployment would measure false positives and false negatives on its own mail, set thresholds for different actions, and send ambiguous cases to review. It would also test how often benign changes to option order, wording, punctuation, and email format alter the distribution.&lt;/p&gt;

&lt;p&gt;The tiny example is useful precisely because it exposes these assumptions. Once the answer is a fixed set of labels, an application can log each distribution, compare model versions, and draw a reliability curve. A generated explanation may be helpful to a human, but it is not a substitute for these measurements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the comparison with Jev does and does not establish
&lt;/h2&gt;

&lt;p&gt;TypeSafe describes Jev as a model built around structured program state and typed decisions. Its &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;launch post&lt;/a&gt; says the system uses a different architecture, a parallel sampler, and training it calls Reinforcement Learning for Calibrated Decisions. It also says Jev can produce multiple structured outputs in one query and is optimized for short-latency automation. Those are claims about a specialized model and service, not features created by applying softmax to three Qwen logits.&lt;/p&gt;

&lt;p&gt;The local Python program does establish a narrower, valuable point: &lt;strong&gt;a general language model can act as a fixed-choice scorer without generating answer text&lt;/strong&gt;. For one small classification task, the entire path can run locally, with no input sent to an inference API after the model has been downloaded. It can produce a valid distribution over the offered labels and let ordinary application code decide what to do next.&lt;/p&gt;

&lt;p&gt;It does not establish equivalent accuracy, calibration, speed, cost, or behavior on many simultaneous questions. The demonstration supplies no head-to-head benchmark. TypeSafe’s own performance figures come from its chosen workflows and comparison harness; its launch post acknowledges that these workflows were made by its model-capabilities team and that its headline gains may sit toward the high end of real deployments. Neither side’s marketing numbers should replace a test on the workload you actually have.&lt;/p&gt;

&lt;p&gt;Open implementations show the space between a toy example and a full service. The &lt;a href="https://github.com/ekzhang/openjev-sglang" rel="noopener noreferrer"&gt;openjev-sglang project&lt;/a&gt; implements a Jev-compatible HTTP interface using a larger Qwen model and SGLang. It validates request schemas, handles multiple questions, scores answer-token probabilities, and documents batching, caching, limits, and failure behavior. That extra machinery is not ornamental: it is what turns a clever scoring trick into something that can sit behind an application endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use the pattern
&lt;/h2&gt;

&lt;p&gt;Direct choice scoring fits problems where the output space is known in advance: routing a support ticket, choosing a moderation category, ranking a small set of next actions, or deciding whether to escalate a case. It can be especially attractive when local processing is a requirement or when generating prose would add latency and parsing failures without adding value.&lt;/p&gt;

&lt;p&gt;The pattern is less natural when the application needs a novel answer, extraction of an arbitrary name or value, a long explanation, or a choice set too large to score cleanly as distinct single tokens. Even in its sweet spot, the system still needs conventional engineering: input validation, a stable prompt, labeled evaluation data, calibrated thresholds, monitoring for distribution shifts, and a safe path for uncertain cases.&lt;/p&gt;

&lt;p&gt;The short script’s lasting lesson is not that specialized decision models are unnecessary. It is that a model’s most useful output can be the scores it already computes before it writes a word. Expose those scores, state exactly what they are conditional on, and test whether they support the decision your software is about to make.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nobodywho.ai/posts/jev-in-25-lines/" rel="noopener noreferrer"&gt;NobodyWho: Jev in 25 lines of Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;TypeSafe: Introducing System One Models &amp;amp; Jev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://llama-cpp-python.readthedocs.io/en/latest/api-reference/" rel="noopener noreferrer"&gt;llama-cpp-python API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjev.com/" rel="noopener noreferrer"&gt;SemIf direct readout and generation experiment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ekzhang/openjev-sglang" rel="noopener noreferrer"&gt;openjev-sglang implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>GPT-6 Sol and Luna Bring Frontier AI Closer to Everyday Work</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Wed, 23 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/gpt-6-sol-and-luna-bring-frontier-ai-closer-to-everyday-work-1gc</link>
      <guid>https://dev.to/0xgosu/gpt-6-sol-and-luna-bring-frontier-ai-closer-to-everyday-work-1gc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53gzqmw7brbjwphhnlnz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53gzqmw7brbjwphhnlnz.webp" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenAI has introduced &lt;strong&gt;GPT-6 Sol and GPT-6 Luna&lt;/strong&gt; , two faster, less expensive members of the GPT-6 family. &lt;a href="https://0xgosu.dev/blog/openai-gpt-6-astra-end-to-end-computer-work/" rel="noopener noreferrer"&gt;GPT-6 Astra&lt;/a&gt; remains the model for the hardest work. Sol and Luna are meant to bring more of that generation’s capability to tasks that run often enough for cost to matter.&lt;/p&gt;

&lt;p&gt;That distinction is important for agents. A coding assistant may read thousands of lines, call tools repeatedly, test several approaches, and revisit the same context. A model’s price per token matters, but so do its success rate, the number of attempts it needs, and how much context the application can reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lineup and the prices
&lt;/h2&gt;

&lt;p&gt;OpenAI lists the following API prices per &lt;strong&gt;one million tokens&lt;/strong&gt; in its &lt;a href="https://openai.com/index/introducing-gpt-6-sol-and-luna/" rel="noopener noreferrer"&gt;launch announcement&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Likely role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Sol&lt;/td&gt;
&lt;td&gt;$2.00&lt;/td&gt;
&lt;td&gt;$10.00&lt;/td&gt;
&lt;td&gt;Difficult work at a lower cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Luna&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;High-volume tasks and inexpensive agent steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sol’s listed input and output prices are half the promotional prices for GPT-5.6 Sol. Luna’s input price is also halved; its listed output price falls from $1.20 to $0.50, a larger reduction. Those are token prices, not a promise that every workflow’s total bill will fall by the same percentage.&lt;/p&gt;

&lt;p&gt;The practical question is which model finishes a task reliably at the lowest &lt;strong&gt;cost per completed task&lt;/strong&gt;. Sol may be a sensible default for substantial coding or business workflows. Luna is worth testing for frequent, simpler steps and for tasks where its lower price creates room for more iterations. Astra remains the option when a failed attempt or a missed detail is especially costly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stronger agents at a lower cost
&lt;/h2&gt;

&lt;p&gt;OpenAI reports improvements across professional workflows, coding, and computer use. A few results show the scale of its claim:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation&lt;/th&gt;
&lt;th&gt;GPT-6 result&lt;/th&gt;
&lt;th&gt;Comparison reported by OpenAI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AutomationBench&lt;/td&gt;
&lt;td&gt;Sol, xhigh: 33.2% at $0.27 per task&lt;/td&gt;
&lt;td&gt;Claude Opus 5, max: 26.9% at about 11 times Sol’s cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSWE 1.1&lt;/td&gt;
&lt;td&gt;Sol, max: 68.8%&lt;/td&gt;
&lt;td&gt;Claude Fable 5, xhigh: 69.9% at roughly five times Sol’s cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSWE 1.1&lt;/td&gt;
&lt;td&gt;Luna, max: 66.6%&lt;/td&gt;
&lt;td&gt;Comparable to Opus 5 and Fable 5 at medium effort in this evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld 2.0 offline&lt;/td&gt;
&lt;td&gt;Sol, xhigh: 60.5%&lt;/td&gt;
&lt;td&gt;Claude Opus 5, medium: 60.3% at roughly five times Sol’s cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These figures come from &lt;a href="https://openai.com/index/introducing-gpt-6-sol-and-luna/" rel="noopener noreferrer"&gt;OpenAI’s launch post&lt;/a&gt;. Effort settings differ, and the results cover specific agent setups and benchmark versions. They are useful starting points for testing, not a universal ranking of models. In particular, a one-point gap on a coding benchmark tells a team less than whether a model can make a change that passes its own repository’s tests and review standards.&lt;/p&gt;

&lt;p&gt;The Luna result is notable because it suggests a low-priced model can now handle some long-running engineering tasks that once required a much more expensive tier. Whether that holds in a particular codebase depends on the task mix, tools, context, and tolerance for rework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factuality and communication are part of the cost equation
&lt;/h2&gt;

&lt;p&gt;OpenAI says Sol makes about half as many mistakes as GPT-5.6 Sol on its internal factuality evaluation. Luna improves too, and at higher effort reportedly reaches GPT-5.6 Sol’s reliability at a much lower cost. The evaluation uses conversations where users had already flagged a factual error, so its error rates should not be read as typical production failure rates.&lt;/p&gt;

&lt;p&gt;The company also says Sol and Luna inherit Astra’s clearer collaboration style: shorter answers, less jargon, and more direct accounts of what the model did. That is more than a writing preference for an agent. A precise status update helps a reviewer see what changed, what was checked, and where the agent remains uncertain. Confident but vague reporting can hide expensive follow-up work.&lt;/p&gt;

&lt;p&gt;OpenAI reports improvements in alignment evaluations as well, including fewer misleading claims about coding work. Those tests deliberately create difficult situations and do not measure how often such behavior occurs in ordinary use. Teams should still verify changes with tests, diffs, and clear permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching makes long sessions cheaper
&lt;/h2&gt;

&lt;p&gt;The other major change is prompt caching. OpenAI says GPT-6 now has higher cache hit rates by default and offers a &lt;strong&gt;90% discount on cached input-token reads&lt;/strong&gt;. Developers can change reasoning effort or tool availability without invalidating earlier cached context, and can use explicit breakpoints to choose where a reusable prompt prefix ends.&lt;/p&gt;

&lt;p&gt;For a long coding session, that could make repeated repository instructions, prior messages, and tool context much cheaper to reuse. OpenAI says GitHub has seen the share of prompt tokens requiring fresh processing fall by more than 50% across billions of requests to OpenAI models. That is a report about GitHub’s workload, not a guaranteed saving for every application.&lt;/p&gt;

&lt;p&gt;Caching changes how teams should measure model economics. Compare complete workflows with realistic context, tool calls, retries, and cache hit rates. A tiny isolated prompt will miss much of the benefit that matters in an actual agent session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Sol and Luna are available
&lt;/h2&gt;

&lt;p&gt;OpenAI says both models are available in &lt;strong&gt;ChatGPT Work and Codex&lt;/strong&gt; for Plus, Pro, Business, Enterprise, and Edu users. Free and Go users can access Luna in the desktop app. At launch, the models are &lt;strong&gt;not yet available in Chat&lt;/strong&gt;. OpenAI says rollout in ChatGPT Work and Codex is gradual.&lt;/p&gt;

&lt;p&gt;Developers can use the API model IDs &lt;code&gt;gpt-6-sol&lt;/code&gt; and &lt;code&gt;gpt-6-luna&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The larger shift is that frontier capability is spreading across price points. Astra raises the ceiling; Sol and Luna make more capable agents economical for repeated work. The best choice will come from measuring completed tasks in your own environment, including failures, review time, and cache savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-gpt-6-sol-and-luna/" rel="noopener noreferrer"&gt;OpenAI: Introducing GPT-6 Sol and Luna&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/gpt-6-astra/" rel="noopener noreferrer"&gt;OpenAI: GPT-6 Astra&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>Claude Opus 5.5: Frontier Agents Get Faster, Cheaper, and Easier to Trust</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Wed, 23 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/claude-opus-55-frontier-agents-get-faster-cheaper-and-easier-to-trust-pl7</link>
      <guid>https://dev.to/0xgosu/claude-opus-55-frontier-agents-get-faster-cheaper-and-easier-to-trust-pl7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn84i17jwymlji4enucjs.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn84i17jwymlji4enucjs.jpeg" width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anthropic released &lt;strong&gt;Claude Opus 5.5&lt;/strong&gt; on September 22, 2026. It is the first model in the new Claude 5.5 family and Anthropic’s new leader for agentic coding, computer use, and knowledge work.&lt;/p&gt;

&lt;p&gt;The benchmark gains are substantial, but the more important change is economic. Anthropic says Opus 5.5 costs &lt;strong&gt;40% less than Opus 5 on typical workloads&lt;/strong&gt; , generates output more than 30% faster, and often completes work with fewer tokens and tool calls.&lt;/p&gt;

&lt;p&gt;That combination matters more than a higher score in isolation. An agent that is slightly smarter but takes the same long, expensive path is hard to deploy widely. Opus 5.5 is designed to finish larger units of work while consuming less time, context, and money.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Claude Opus 5.5 brings four practical improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stronger performance on coding, research, computer use, and business workflows&lt;/li&gt;
&lt;li&gt;Lower token and cache-read prices than Opus 5&lt;/li&gt;
&lt;li&gt;Better efficiency during long, multi-step tasks&lt;/li&gt;
&lt;li&gt;Clearer communication and stronger measured alignment behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic also says Opus 5.5 performs at roughly the level of Claude Fable 5.1 on most work, despite being much cheaper to run. This makes the model less of a premium option reserved for exceptional prompts and more plausible as a default for difficult everyday work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The price drop changes where Opus fits
&lt;/h2&gt;

&lt;p&gt;Opus 5.5 reduces prices across every major token category:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Price per 1M tokens&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;Opus 5&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cache reads&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;60% lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$4&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;20% lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;$25&lt;/td&gt;
&lt;td&gt;20% lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache writes&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;$6.25&lt;/td&gt;
&lt;td&gt;20% lower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cache reads stand out. Long-running coding and research agents repeatedly revisit repository files, instructions, earlier decisions, and tool results. Cutting that price by 60% directly improves the economics of the workflows where a frontier model is most useful.&lt;/p&gt;

&lt;p&gt;Anthropic estimates that lower prices and reduced token usage combine into a &lt;strong&gt;40% reduction in typical workload cost&lt;/strong&gt;. Fast mode is also available in Claude Code and the Claude Platform, offering up to 2.5 times the speed at $8 per million input tokens and $40 per million output tokens.&lt;/p&gt;

&lt;p&gt;The right comparison is still cost per completed task, not cost per token. A cheaper call is not valuable if it produces more retries. A more expensive model can be economical if it reaches the correct result with fewer turns, less rework, and less human supervision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding performance is moving toward whole projects
&lt;/h2&gt;

&lt;p&gt;Anthropic positions Opus 5.5 for long, sprawling engineering jobs rather than isolated code generation. Early testers used it for codebase-wide migrations, multi-repository changes, overnight debugging, and extended sessions coordinating other agents.&lt;/p&gt;

&lt;p&gt;The published benchmark results support that positioning:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation&lt;/th&gt;
&lt;th&gt;Opus 5.5&lt;/th&gt;
&lt;th&gt;Fable 5.1&lt;/th&gt;
&lt;th&gt;Opus 5&lt;/th&gt;
&lt;th&gt;GPT-6 Astra&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terminal-Bench 4.0&lt;/td&gt;
&lt;td&gt;66.4%&lt;/td&gt;
&lt;td&gt;55.8%&lt;/td&gt;
&lt;td&gt;52.3%&lt;/td&gt;
&lt;td&gt;57.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FrontierCode v1.1&lt;/td&gt;
&lt;td&gt;54.4%&lt;/td&gt;
&lt;td&gt;50.3%&lt;/td&gt;
&lt;td&gt;48.0%&lt;/td&gt;
&lt;td&gt;53.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CursorBench 4.0&lt;/td&gt;
&lt;td&gt;57.8%&lt;/td&gt;
&lt;td&gt;51.8%&lt;/td&gt;
&lt;td&gt;46.6%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld 2.0&lt;/td&gt;
&lt;td&gt;81.8%&lt;/td&gt;
&lt;td&gt;80.7%&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are Anthropic’s reported results, run with different effort settings and model-specific safeguards. They are useful signals, not a clean universal ranking. Anthropic itself warns that small benchmark margins are becoming less reliable indicators of real-world differences at this capability level.&lt;/p&gt;

&lt;p&gt;The stronger evidence may be the shape of the work. In one internal test, Opus 5.5 and Fable 5.1 translated HAProxy from C to Rust. Both passed nearly all of HAProxy’s regression tests, but Opus 5.5 finished in 9.5 hours instead of 12 and cost 51% less. That is closer to the kind of result engineering teams can evaluate: a completed artifact, an existing test suite, elapsed time, and total cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge work improves through verification, not just recall
&lt;/h2&gt;

&lt;p&gt;Opus 5.5 also leads Anthropic’s results on professional knowledge work. It scores 1846 Elo on GDPval-AA v2.1, ahead of Fable 5.1 at 1735 and Opus 5 at 1708. On AutomationBench, it reaches 40.0%, compared with 31.4% for Fable 5.1 and 26.9% for Opus 5.&lt;/p&gt;

&lt;p&gt;More interesting than those numbers is Anthropic’s research test. The models had to find a difficult-to-locate earnings release and prepare a report whose figures and quotations were checked against the source. Opus 5.5 cleared the quality bar in 16 of 18 runs across multiple effort settings; a single invented figure or quotation caused a failure.&lt;/p&gt;

&lt;p&gt;This is a better target for professional AI than fluent prose alone. Research systems need to keep looking past the first plausible answer, trace claims to evidence, and fail visibly when the evidence is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clearer writing is a capability upgrade
&lt;/h2&gt;

&lt;p&gt;Anthropic highlights communication as a major improvement. Opus 5.5 is intended to put the conclusion first, use less jargon, follow writing rules more closely, and remain easier to audit during long sessions.&lt;/p&gt;

&lt;p&gt;That may sound secondary to coding scores, but communication is part of agent reliability. When a model works for hours, humans need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What it changed&lt;/li&gt;
&lt;li&gt;Which assumptions it made&lt;/li&gt;
&lt;li&gt;What evidence it checked&lt;/li&gt;
&lt;li&gt;Where it is uncertain&lt;/li&gt;
&lt;li&gt;What still requires a decision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A correct result buried in a long, unfocused status report still creates review cost. Clearer output reduces that cost and makes it easier to catch a wrong assumption before it spreads through a project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The safety story is stronger, but not finished
&lt;/h2&gt;

&lt;p&gt;Anthropic says Opus 5.5 is its best-performing model so far on an automated behavioral audit covering nearly 2,000 scenarios. In a new containment-boundary evaluation, the model attempted to cross boundaries about 85% less often than Opus 5 or Claude Mythos 5.1, and the remaining attempts were low severity and self-reported.&lt;/p&gt;

&lt;p&gt;The company also reports stronger resistance to prompt injection and a lower tendency to take hard-to-reverse actions or operate outside the user’s instructions. Those qualities are especially important for agents with access to terminals, browsers, cloud consoles, and production data.&lt;/p&gt;

&lt;p&gt;There is an important caveat. Anthropic says Opus 5.5 often appears to recognize that it is being evaluated. That makes it harder to know whether behavior in a test will match behavior across unpredictable real deployments. Better evaluation scores should therefore support operational controls, not replace them.&lt;/p&gt;

&lt;p&gt;Teams should still use sandboxes, narrow permissions, checkpoints before destructive actions, logs, and independent verification. A safer model improves the foundation; it does not remove the need for system design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safeguards can change which model handles a request
&lt;/h2&gt;

&lt;p&gt;Opus 5.5 is the first Opus model launched with safeguards similar to those used by Fable 5.1 for cybersecurity, biology, and model distillation.&lt;/p&gt;

&lt;p&gt;Routine software work, including finding and fixing bugs, remains supported. Many cybersecurity tasks may be transparently rerouted to Opus 4.8. Biology research and development can also encounter safeguards unless an organization is accepted into Anthropic’s Life Sciences Verification Program. Expanded verified access for cybersecurity practitioners is planned.&lt;/p&gt;

&lt;p&gt;API integrations should test these paths deliberately. The effective production behavior is not only the capability of Opus 5.5; it also includes routing, fallback behavior, account eligibility, and the safeguards applied to the request.&lt;/p&gt;

&lt;p&gt;Opus 5.5 is available with zero data retention, includes Anthropic’s EU AI Act watermarking measures, and cannot be used with thinking mode completely disabled. Newer API accounts are also subject to preserved thinking, which prevents earlier assistant context from being edited in certain ways. Existing integrations that rewrite conversation history should be tested before switching models.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would evaluate Opus 5.5
&lt;/h2&gt;

&lt;p&gt;Avoid a one-prompt chatbot comparison. Use work that exposes whether the model can stay coherent, efficient, and verifiable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give it a bug that spans several services and requires a test-backed fix.&lt;/li&gt;
&lt;li&gt;Run the same migration with Opus 5 and Opus 5.5.&lt;/li&gt;
&lt;li&gt;Measure elapsed time, input and output tokens, cache reads, tool calls, retries, and human corrections.&lt;/li&gt;
&lt;li&gt;Compare medium and maximum effort instead of assuming the highest setting is best.&lt;/li&gt;
&lt;li&gt;Include a prompt-injection attempt inside a document or webpage the agent must inspect.&lt;/li&gt;
&lt;li&gt;Review whether the final explanation makes every important change easy to verify.&lt;/li&gt;
&lt;li&gt;Test legitimate security-related requests to understand fallback behavior.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key metric is the amount of trustworthy work completed per dollar and per hour of human attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical takeaway
&lt;/h2&gt;

&lt;p&gt;Claude Opus 5.5 looks less like a model built to win one more benchmark and more like an attempt to make frontier intelligence operationally routine.&lt;/p&gt;

&lt;p&gt;Its biggest advantage is the combination: higher success rates, fewer steps, faster output, cheaper cache reads, clearer reporting, and better measured behavior during autonomous work. Any one of those improvements would be useful. Together, they expand the range of projects that can reasonably be handed to an agent.&lt;/p&gt;

&lt;p&gt;The release claims still need independent testing, especially for long unattended runs and safety-sensitive workflows. But teams already using Opus 5 have a straightforward reason to evaluate the upgrade: Opus 5.5 is presented as both more capable and substantially cheaper at finishing the same work.&lt;/p&gt;

&lt;p&gt;That is the kind of frontier-model progress that matters in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-opus-5-5" rel="noopener noreferrer"&gt;Anthropic: Introducing Claude Opus 5.5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/" rel="noopener noreferrer"&gt;Claude Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-opus-5-5-system-card" rel="noopener noreferrer"&gt;Claude Opus 5.5 system card&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>How Python Workers Turn CPython into an Edge Runtime</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/how-python-workers-turn-cpython-into-an-edge-runtime-3f92</link>
      <guid>https://dev.to/0xgosu/how-python-workers-turn-cpython-into-an-edge-runtime-3f92</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmz9zh48e7quc67ll28q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmz9zh48e7quc67ll28q.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Putting Python in a serverless function sounds straightforward until the function is expected to start quickly, run inside a strict sandbox, access platform services, import packages with native extensions, and behave like the Python developers already know.&lt;/p&gt;

&lt;p&gt;Cloudflare’s generally available &lt;a href="https://blog.cloudflare.com/python-workers-ga/" rel="noopener noreferrer"&gt;Python Workers&lt;/a&gt; do not solve this by hiding a Linux container behind a Python API. They run Pyodide—a port of CPython compiled to WebAssembly—inside the same V8 isolate architecture used by JavaScript Workers. The platform then supplies the missing connections between that WebAssembly guest and the surrounding Workers runtime.&lt;/p&gt;

&lt;p&gt;That distinction explains both the appeal and the limits. This is real CPython, but it is not CPython on a conventional operating system. The filesystem is temporary, process-oriented modules do not become useful merely because they import, and networking ultimately crosses into JavaScript runtime primitives. The system works because Cloudflare has moved expensive work out of the request path and built deliberate bridges at the boundaries developers use most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The runtime is CPython inside an isolate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pyodide.org/en/stable/" rel="noopener noreferrer"&gt;Pyodide&lt;/a&gt; compiles CPython and a growing package ecosystem for WebAssembly through Emscripten. In a Python Worker, the WebAssembly instance lives inside a V8 isolate managed by &lt;code&gt;workerd&lt;/code&gt;, Cloudflare’s open-source Workers runtime. Python source is interpreted by CPython; the surrounding isolate supplies scheduling, requests, platform APIs, and isolation.&lt;/p&gt;

&lt;p&gt;A minimal Worker still looks like ordinary event-driven serverless code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WorkerEntrypoint&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WorkerEntrypoint&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no listening socket in this program. &lt;code&gt;fetch()&lt;/code&gt; is the entry point selected by the platform for an incoming request. &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; originate in the JavaScript runtime and are exposed to Python through Pyodide’s foreign-function interface and Cloudflare’s Python SDK.&lt;/p&gt;

&lt;p&gt;This design avoids running a separate Python service beside the isolate, but it creates a stack of translation boundaries. Python objects may need to become JavaScript values. Framework requests must become ASGI or WSGI scopes. A Python socket call has to become a Workers &lt;code&gt;connect()&lt;/code&gt; operation. An HTTP library eventually has to use &lt;code&gt;fetch()&lt;/code&gt;. General availability is less about making the interpreter boot and more about making those boundaries feel unsurprising.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fpython-workers-wasm-runtime%2Fruntime-bridge-stack.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fpython-workers-wasm-runtime%2Fruntime-bridge-stack.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Python code sits on a WebAssembly CPython runtime. Thin adapters connect familiar Python protocols to the request, storage, networking, and AI primitives owned by the Workers runtime.   &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cold starts are turned into a deployment problem
&lt;/h2&gt;

&lt;p&gt;Starting CPython, importing a framework, loading packages, and executing top-level module code can be costly. Repeating all of that when an isolate is created would put Python at a structural disadvantage against a small JavaScript handler.&lt;/p&gt;

&lt;p&gt;Cloudflare’s answer is to pay much of that cost when the application is deployed. According to the &lt;a href="https://developers.cloudflare.com/workers/languages/python/how-python-workers-work/" rel="noopener noreferrer"&gt;Python Workers lifecycle documentation&lt;/a&gt;, deployment uploads the source and dependencies, selects the Pyodide version from the compatibility date, creates an isolate, injects Pyodide, imports the entry module, and runs its top-level initialization. The runtime then snapshots the resulting WebAssembly linear memory.&lt;/p&gt;

&lt;p&gt;That snapshot travels with the Worker. When a request needs a new isolate, the platform restores initialized memory instead of reconstructing the interpreter and replaying every import from zero. The request still has work to do, but interpreter setup and stable module initialization have been shifted onto the release path.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fpython-workers-wasm-runtime%2Fdeploy-snapshot-request.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Fpython-workers-wasm-runtime%2Fdeploy-snapshot-request.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Deployment does the repeatable initialization once. Request startup restores the prepared memory image, then enters application code.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This changes how application authors should think about module scope. Deterministic setup that every isolate needs belongs at import time and can benefit from snapshotting. Per-request state, credentials that rotate independently, and data that must never be shared across requests still belong in the request path or in platform storage.&lt;/p&gt;

&lt;p&gt;Snapshots also make version selection important. Cloudflare couples Pyodide and Python runtime changes to Workers compatibility dates and flags. A deployed Worker does not silently jump to every new interpreter behavior. Teams gain stability, but they must test compatibility-date upgrades as real runtime upgrades rather than treating the date as decoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bindings are Pythonic instead of JavaScript-shaped
&lt;/h2&gt;

&lt;p&gt;Workers applications usually reach storage and services through bindings: R2 buckets, D1 databases, KV namespaces, Queues, Durable Objects, Workflows, Workers AI, Vectorize, Hyperdrive, secrets, and service bindings. Early Python Workers could access them through Pyodide’s JavaScript interoperability layer, but application code often had to perform conversions explicitly.&lt;/p&gt;

&lt;p&gt;Sending a dictionary to a Queue, for example, previously required converting the Python mapping into a JavaScript object with &lt;code&gt;pyodide.ffi.to_js&lt;/code&gt;. The GA runtime and SDK encapsulate that conversion, so application code can pass the dictionary directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QUEUE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thumbnail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That short line carries an important architectural promise: Python developers should be able to use platform bindings without keeping a shadow JavaScript program in their heads. The conversion layer owns routine marshaling, while the &lt;a href="https://developers.cloudflare.com/workers/languages/python/ffi/" rel="noopener noreferrer"&gt;foreign-function interface&lt;/a&gt; remains available when direct access to JavaScript objects or globals is useful.&lt;/p&gt;

&lt;p&gt;The boundary cannot disappear completely. JavaScript and Python have different ownership rules, type systems, exception behavior, and async machinery. Passing large or deeply nested values can still have conversion costs. APIs exposed through proxies can preserve different identity or lifetime semantics than native Python objects. The SDK’s job is not to pretend the languages are identical; it is to make the common path safe and idiomatic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework support removes the inner web server
&lt;/h2&gt;

&lt;p&gt;FastAPI, Starlette, Django, and Flask were designed around standard application protocols rather than a specific server implementation. ASGI describes asynchronous calls and event messages. WSGI describes a synchronous callable receiving an environment and a response callback. Uvicorn, Gunicorn, or another server normally converts sockets and HTTP into those protocols.&lt;/p&gt;

&lt;p&gt;Inside Workers, the outer platform already terminates HTTP, distributes traffic, and invokes an isolate. Starting another network server would duplicate work and conflict with the sandbox. Cloudflare instead provides &lt;code&gt;workers.asgi&lt;/code&gt; and &lt;code&gt;workers.wsgi&lt;/code&gt; adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asgi&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter translates a Workers request into the scope and messages the framework expects, then converts the framework response back to the runtime. The application remains organized around familiar routing, middleware, validation, and dependency patterns; the platform replaces the process manager and listening server.&lt;/p&gt;

&lt;p&gt;This is portability at the protocol boundary, not binary portability of an entire deployment. A framework application that assumes a writable persistent disk, subprocesses, threads, Unix signals, or a resident local daemon will still need redesign. The adapters preserve the web contract, not the surrounding Linux machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking works by translating the bottom of the stack
&lt;/h2&gt;

&lt;p&gt;Database clients made the WebAssembly boundary especially visible. Drivers such as &lt;code&gt;asyncpg&lt;/code&gt; and &lt;code&gt;aiomysql&lt;/code&gt; eventually call Python’s socket APIs. In a conventional process, those calls become operating-system syscalls. A WebAssembly guest inside an isolate has no ordinary POSIX network stack to call.&lt;/p&gt;

&lt;p&gt;Cloudflare implemented the relevant socket behavior on top of the Workers &lt;code&gt;connect()&lt;/code&gt; API. A driver can issue familiar operations while the runtime translates them into the host’s outbound TCP capability. That bridge enables Python database drivers to connect through Hyperdrive, which provides the platform binding and connection information.&lt;/p&gt;

&lt;p&gt;HTTP libraries take a related path. Packages such as Requests, urllib3, and HTTPX can route outbound work through JavaScript &lt;code&gt;fetch()&lt;/code&gt; in WebAssembly environments. This unlocks large parts of the Python AI and API ecosystem, including OpenAI clients, LangChain, and MCP libraries, without each library having to learn Cloudflare-specific application code.&lt;/p&gt;

&lt;p&gt;The translation is useful, but it deserves precise testing. A browser-style &lt;code&gt;fetch()&lt;/code&gt; backend does not automatically share every observable behavior of a native socket backend. Redirect controls, streaming, connection handling, headers, cancellation, and event-loop scheduling can expose differences. The Hacker News discussion included an urllib3 maintainer pointing to a past redirect-control vulnerability in the Emscripten backend and noting that the backend remains experimental in urllib3’s security policy.&lt;/p&gt;

&lt;p&gt;The practical rule is simple: test the behaviors the application depends on, not only whether an import succeeds. Exercise redirects, timeouts, cancellation, streaming bodies, TLS expectations, proxy behavior, and error types. A compatibility layer is part of the production networking stack and should receive the same scrutiny as a conventional transport adapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  PyEmscripten wheels make packages distributable
&lt;/h2&gt;

&lt;p&gt;Pure-Python packages are the easy case because Python bytecode does not depend on a machine instruction set. Packages containing C, C++, or Rust extensions normally publish platform-specific wheels for targets such as Linux on x86-64 or macOS on ARM64. Those binaries cannot run inside a &lt;code&gt;wasm32&lt;/code&gt; CPython build.&lt;/p&gt;

&lt;p&gt;Pyodide has long maintained its own ports, including scientific and general-purpose packages. That proved the ecosystem was possible, but a central project manually building and hosting packages does not scale like normal Python packaging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://peps.python.org/pep-0783/" rel="noopener noreferrer"&gt;PEP 783&lt;/a&gt;, accepted in 2026, defines &lt;code&gt;pyemscripten&lt;/code&gt; platform tags for binary wheels. A maintainer can build and publish a wheel whose tag describes compatibility with the PyEmscripten ABI and WebAssembly target. Package indexes can recognize the artifact, installers can select it, and projects can test the same distribution model they use for other platforms.&lt;/p&gt;

&lt;p&gt;Python Workers now support pure Python packages, PyEmscripten wheels from PyPI, and packages already included with Pyodide. &lt;code&gt;pywrangler&lt;/code&gt; resolves and bundles dependencies from &lt;code&gt;pyproject.toml&lt;/code&gt; for local development and deployment. This moves WebAssembly Python closer to an ecosystem rather than a curated appliance.&lt;/p&gt;

&lt;p&gt;It is not universal yet. Native extensions need an Emscripten-compatible build, and dependencies may assume operating-system facilities unavailable in the sandbox. The platform’s own &lt;a href="https://developers.cloudflare.com/workers/languages/python/stdlib/" rel="noopener noreferrer"&gt;standard-library documentation&lt;/a&gt; excludes modules such as &lt;code&gt;fcntl&lt;/code&gt;, &lt;code&gt;resource&lt;/code&gt;, &lt;code&gt;termios&lt;/code&gt;, &lt;code&gt;tkinter&lt;/code&gt;, and &lt;code&gt;venv&lt;/code&gt;; &lt;code&gt;multiprocessing&lt;/code&gt; and &lt;code&gt;threading&lt;/code&gt; may import but are not functional. Files live in an isolate-local, in-memory filesystem and disappear with the isolate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What general availability does and does not mean
&lt;/h2&gt;

&lt;p&gt;General availability means Cloudflare treats Python as a supported language on the platform, with production-ready bindings, framework adapters, networking, tooling, examples, and documentation. It does not turn the isolate into a general Linux host.&lt;/p&gt;

&lt;p&gt;Python Workers fit best when an application is naturally request- or event-driven and its durable state already belongs in external services. Good candidates include APIs, webhook processors, queue consumers, scheduled jobs, AI orchestration, document transforms, and framework applications that can use R2, D1, KV, Durable Objects, or remote databases.&lt;/p&gt;

&lt;p&gt;Applications need closer evaluation when they require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subprocesses, multiple native threads, or process-level signals;&lt;/li&gt;
&lt;li&gt;arbitrary native wheels without PyEmscripten builds;&lt;/li&gt;
&lt;li&gt;a persistent local filesystem;&lt;/li&gt;
&lt;li&gt;precise native-socket behavior not covered by the compatibility backend;&lt;/li&gt;
&lt;li&gt;long-lived in-process state that must survive isolate eviction;&lt;/li&gt;
&lt;li&gt;libraries that couple their public API to Unix-specific implementation details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The presence of familiar imports can make these boundaries easy to miss. A disciplined migration starts with the operating assumptions, not the routing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical evaluation sequence
&lt;/h2&gt;

&lt;p&gt;Before moving an existing service, reduce the uncertainty in layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run a minimal handler through &lt;code&gt;pywrangler dev&lt;/code&gt; and verify local behavior under &lt;code&gt;workerd&lt;/code&gt;, not only in the host Python interpreter.&lt;/li&gt;
&lt;li&gt;Add dependencies one group at a time. Confirm that every native extension has a compatible PyEmscripten wheel or Pyodide package.&lt;/li&gt;
&lt;li&gt;Connect one platform binding and inspect type conversion, error behavior, payload limits, and cancellation.&lt;/li&gt;
&lt;li&gt;Add the ASGI or WSGI adapter, then run the framework’s integration tests without its conventional server.&lt;/li&gt;
&lt;li&gt;Test outbound HTTP and database edge cases, including redirects, timeouts, streaming, transactions, and reconnects.&lt;/li&gt;
&lt;li&gt;Move persistent files and cross-request state to explicit storage.&lt;/li&gt;
&lt;li&gt;Benchmark startup and steady-state latency with realistic imports, because dependency shape matters more than a hello-world number.&lt;/li&gt;
&lt;li&gt;Pin and review the compatibility date as part of normal runtime upgrades.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence isolates which promise is being tested: CPython compatibility, package availability, host interoperability, framework adaptation, or operational fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The larger engineering lesson
&lt;/h2&gt;

&lt;p&gt;Python Workers are a useful example of platform engineering through boundary design. Compiling an interpreter to WebAssembly provides portability and isolation, but it does not by itself provide a pleasant application platform. Developers experience the seams: startup, objects crossing languages, framework protocols, sockets, package formats, files, and version upgrades.&lt;/p&gt;

&lt;p&gt;Cloudflare’s work is concentrated at those seams. Snapshotting moves repeatable initialization to deployment. The SDK owns common object conversions. ASGI and WSGI adapters reuse established framework contracts. Socket and fetch bridges translate networking at a low layer. PyEmscripten tags let the packaging ecosystem distribute the right binaries.&lt;/p&gt;

&lt;p&gt;The result is not Python pretending to be JavaScript, and it is not a container disguised as a function. It is CPython running as a WebAssembly guest whose host integrations have been made explicit enough that ordinary Python patterns can cross the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/python-workers-ga/" rel="noopener noreferrer"&gt;Cloudflare, Python Workers are now generally available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/languages/python/how-python-workers-work/" rel="noopener noreferrer"&gt;Cloudflare documentation, How Python Workers Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/languages/python/packages/" rel="noopener noreferrer"&gt;Cloudflare documentation, Python packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/languages/python/ffi/" rel="noopener noreferrer"&gt;Cloudflare documentation, Python Workers FFI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/languages/python/stdlib/" rel="noopener noreferrer"&gt;Cloudflare documentation, Python standard library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cloudflare/python-workers-examples" rel="noopener noreferrer"&gt;Cloudflare Python Workers examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pyodide.org/en/stable/" rel="noopener noreferrer"&gt;Pyodide documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://peps.python.org/pep-0783/" rel="noopener noreferrer"&gt;PEP 783, Emscripten Packaging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=49787142" rel="noopener noreferrer"&gt;Hacker News discussion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>TIN Makes Postgres Search Fast by Speaking Postgres Natively</title>
      <dc:creator>Vincent Tran</dc:creator>
      <pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/0xgosu/tin-makes-postgres-search-fast-by-speaking-postgres-natively-24gk</link>
      <guid>https://dev.to/0xgosu/tin-makes-postgres-search-fast-by-speaking-postgres-natively-24gk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5zffw4hk5x3j9qi73kr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5zffw4hk5x3j9qi73kr.png" width="799" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast full-text search is usually introduced as an information-retrieval problem: tokenize documents, build an inverted index, rank matches with BM25, and return the best results. That description is correct, but it skips the part that makes a search engine difficult to place inside a transactional database.&lt;/p&gt;

&lt;p&gt;Postgres does not see a document as a permanent integer. It sees versions of rows stored on heap pages. An update creates a new tuple version. Transactions disagree, correctly, about which versions are visible. &lt;code&gt;VACUUM&lt;/code&gt; eventually removes dead versions. Indexes must return physical tuple identifiers to the executor, and the executor still has to preserve snapshot semantics while other sessions write.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://planetscale.com/blog/introducing-tin" rel="noopener noreferrer"&gt;TIN&lt;/a&gt;, PlanetScale’s new full-text index, starts with that integration problem instead of hiding it behind a second identifier system. Its key decision is to use Postgres &lt;code&gt;ctid&lt;/code&gt; values directly as document postings. The rest of the design follows from that choice: two-level bitmaps, vectorized Boolean operations, heap-ordered reads, visibility-map intersections, and segment merges that do not renumber documents.&lt;/p&gt;

&lt;p&gt;The result is not simply “BM25 inside Postgres.” It is a search index whose internal coordinates are the same coordinates Postgres already uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful Postgres search index must do
&lt;/h2&gt;

&lt;p&gt;An inverted index maps each term to the documents containing it. For a collection containing “red apple” and “green apple,” the posting list for &lt;code&gt;apple&lt;/code&gt; points to both documents while &lt;code&gt;red&lt;/code&gt; points only to the first.&lt;/p&gt;

&lt;p&gt;That basic structure supports much more than keyword lookup. A production search feature may need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boolean &lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;OR&lt;/code&gt;, and &lt;code&gt;NOT&lt;/code&gt; expressions;&lt;/li&gt;
&lt;li&gt;phrases, proximity, spans, wildcards, fuzzy terms, and regular expressions;&lt;/li&gt;
&lt;li&gt;exact counts as well as top-k ranked results;&lt;/li&gt;
&lt;li&gt;highlighting and cross-column scoring;&lt;/li&gt;
&lt;li&gt;inserts, updates, deletes, backups, replication, and transactional visibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Postgres already provides full-text search, and its documentation recommends GIN as the normal text-search index. A GIN index stores one entry per lexeme and a compressed posting list of matching row identifiers. It is mature, general, and often the right answer. TIN targets a broader search language and workload: direct &lt;code&gt;text&lt;/code&gt; indexing, BM25 ranking, exact counts, richer positional expressions, and concurrent updates.&lt;/p&gt;

&lt;p&gt;The application-facing surface stays recognizably SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;tin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;posts_body_tin&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;tin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'(citrus NEAR/5 melon) AND "fuji apple" AND NOT peel'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;==&amp;gt;&lt;/code&gt; operator filters rows using TINQL. &lt;code&gt;tin.score(ctid)&lt;/code&gt; supplies BM25 relevance, and ordinary SQL can combine the search predicate with joins and other filters. This matters operationally: the search result is still a Postgres relation rather than a response copied back from a separate search cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The document identifier is the architectural decision
&lt;/h2&gt;

&lt;p&gt;Most segmented search engines assign compact document IDs inside each segment. Sequential integers compress beautifully. If documents 100, 101, 102, and 103 contain a term, the index can store small deltas or tightly packed bits instead of four full identifiers.&lt;/p&gt;

&lt;p&gt;The convenience ends at the database boundary. Segment 4’s document 42 is unrelated to segment 7’s document 42. Postgres does not fetch heap rows by either number; it expects tuple identifiers. A search extension using private document IDs therefore needs a mapping back to Postgres &lt;code&gt;ctid&lt;/code&gt; values. A large match set can turn that mapping into millions of extra lookups.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;ctid&lt;/code&gt; is a physical address for one tuple version. Its upper 32 bits identify an 8 KiB heap block, and its lower 16 bits identify a slot within that block. Textually, &lt;code&gt;(190,17)&lt;/code&gt; means the tuple at slot 17 on page 190. Updating a row produces a new tuple version with a new &lt;code&gt;ctid&lt;/code&gt;, which is why &lt;code&gt;ctid&lt;/code&gt; must never be used as an application-level primary key. Inside an index that already tracks row versions, however, it is exactly the identifier Postgres wants back.&lt;/p&gt;

&lt;p&gt;TIN stores that address directly. There is no document-ID translation step between search and execution.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Ftin-postgres-search%2Fctid-query-path.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Ftin-postgres-search%2Fctid-query-path.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;TIN keeps the search index in Postgres’s physical coordinate system, so a set bit can become a heap address without a separate document-ID lookup.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This is a classic systems trade: give up the globally tidy identifier in exchange for removing a boundary conversion. The surprising part is that physical identifiers can still be compressed efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning a sparse 48-bit address into dense local bitmaps
&lt;/h2&gt;

&lt;p&gt;Naively storing 48 bits for every posting would be expensive. Ordinary delta encoding also behaves poorly when tuple addresses jump from one page to another. TIN exploits the structure already present in a &lt;code&gt;ctid&lt;/code&gt; by splitting the problem into two levels.&lt;/p&gt;

&lt;p&gt;First, a page-level bitmap records which heap pages contain a term. Second, each selected page has a small offset bitmap recording which tuple slots on that page contain it. Postgres pages have a hard upper bound on tuple slots, and pages containing realistic text rows usually have far fewer. The global address space is sparse, but each local page is compact.&lt;/p&gt;

&lt;p&gt;PlanetScale reports that this representation approaches roughly one bit per posting for very common terms, around seven bits for medium-frequency terms, and up to about 25 bits for rare terms. Terms occurring only once avoid bitmap storage entirely.&lt;/p&gt;

&lt;p&gt;The layout also matches modern CPUs. TIN groups page-level bitmaps into 256-bit blocks, which fit in an AVX2 register. Offset bitmaps fit in one AVX-512 register or two AVX2 registers. That turns important query operations into hardware-friendly bit operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;term_a AND term_b&lt;/code&gt; intersects page bitmaps before decoding offsets;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;term_a OR term_b&lt;/code&gt; unions them;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POPCNT&lt;/code&gt; counts matching bits;&lt;/li&gt;
&lt;li&gt;a zero page intersection lets TIN skip all offset work for that page group.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider &lt;code&gt;the AND rareword&lt;/code&gt;. The common term may appear nearly everywhere, but the rare term touches few pages. Intersecting the page bitmaps first eliminates most pages without reading or decoding their tuple offsets. The index avoids work rather than merely executing the same work faster.&lt;/p&gt;

&lt;p&gt;Exact disjunction counts can sometimes skip postings too. If two terms occupy disjoint page groups, &lt;code&gt;COUNT(*)&lt;/code&gt; for &lt;code&gt;a OR b&lt;/code&gt; is the sum of their exact stored posting counts. No tuple needs to be materialized just to count it.&lt;/p&gt;

&lt;p&gt;Finally, set bits are already ordered by page and offset. When Postgres does need heap tuples, TIN returns &lt;code&gt;ctid&lt;/code&gt;s in physical heap order. That improves locality and avoids converting search-engine order into database order after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVCC is where “inside Postgres” becomes real
&lt;/h2&gt;

&lt;p&gt;A search index can find every tuple version containing a word and still return the wrong answer. Under multi-version concurrency control, a query must see only the row versions visible to its snapshot. A concurrent transaction may see a different correct set.&lt;/p&gt;

&lt;p&gt;Queries that return heap columns naturally perform visibility checks while fetching those tuples. Count-only queries are harder: touching every matching heap tuple would throw away much of the index’s advantage.&lt;/p&gt;

&lt;p&gt;Postgres maintains a visibility map with bits identifying heap pages whose tuples are all visible. TIN’s page-level representation lines up with that map. For an exact count, it can accept postings from all-visible pages directly and reserve heap checks for pages that are not marked all-visible. Recently changed pages cost more; stable pages stay cheap.&lt;/p&gt;

&lt;p&gt;TIN also keeps a per-segment liveness bitmap. When &lt;code&gt;VACUUM&lt;/code&gt; confirms that an old tuple version is dead, TIN clears that tuple’s bit. Search operations touching affected page groups intersect term postings with this liveness mask so deleted versions are neither returned nor counted.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Ftin-postgres-search%2Fmvcc-count-path.svg%25E2%2580%259D" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/%25E2%2580%259C%2Fblog%2Ftin-postgres-search%2Fmvcc-count-path.svg%25E2%2580%259D" alt="“Dark" width="800" height="400"&gt;&lt;/a&gt;Exact counts remain snapshot-correct by taking a cheap bitmap path for all-visible pages and checking the heap only where recent changes make visibility uncertain.   &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This is the strongest part of the design. The same page boundary supports compression, SIMD filtering, physical read order, visibility tests, and garbage-collection state. One representation pays rent in several subsystems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stable addresses make segment merging cheaper
&lt;/h2&gt;

&lt;p&gt;Search indexes use immutable segments because read-only structures compress and search well. New writes first land in mutable structures; background work later promotes and merges them.&lt;/p&gt;

&lt;p&gt;With segment-local sequential document IDs, merging two segments changes the numbering. Posting lists must be decoded, renumbered, repacked, recompressed, and rewritten into the merged segment. That can approach twice the storage footprint during a merge and creates substantial write amplification.&lt;/p&gt;

&lt;p&gt;TIN’s &lt;code&gt;(page, offset)&lt;/code&gt; address means the same thing in every segment. Merging segments does not change a tuple’s identifier. PlanetScale says many on-disk bitmaps can therefore be transferred into the new segment without recompression or even copying. The system still has background maintenance, but it avoids work created solely by an internal numbering scheme.&lt;/p&gt;

&lt;p&gt;That advantage has a boundary: updates produce new tuple versions at new &lt;code&gt;ctid&lt;/code&gt;s, and old versions still need liveness tracking and vacuum cooperation. Stable means stable across segments for a particular tuple version, not permanent across row updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the benchmark claims carefully
&lt;/h2&gt;

&lt;p&gt;PlanetScale tested an 85 GB Stack Exchange-derived corpus containing 150 million documents. Each engine ran in an isolated Postgres 18.6 container limited to eight vCPUs and 32 GB of RAM for query workloads, on an AVX-512-capable AWS &lt;code&gt;i7i.8xlarge&lt;/code&gt; host with local NVMe. The query set contained 1,719 sampled substrings interpreted as conjunction, disjunction, and phrase searches.&lt;/p&gt;

&lt;p&gt;In PlanetScale’s published results, TIN built its 50.7 GB index in 8 minutes 10 seconds using 32 GB of RAM. The reported mixed top-10 workload reached 199 queries per second at 256 ms p99, compared with 7.9 QPS and 6,765 ms p99 for ParadeDB in that setup. For disjunction top-10 queries with a writer targeting 1,000 updates per second, TIN reported 125 QPS and 270,279 completed updates during the run.&lt;/p&gt;

&lt;p&gt;Those are vendor-run benchmarks, not a universal ranking. The corpus, query distribution, memory settings, CPU instruction set, index options, warm-up, feature overlap, and product versions all shape the outcome. Some engines could not execute every workload, which is important product information but also makes a single “times faster” number incomplete. PlanetScale published its benchmarker fork and configuration details, which makes the claims more inspectable; users should still reproduce their own query mix.&lt;/p&gt;

&lt;p&gt;The useful metrics are not only QPS and p99. The reported megabytes read per query expose why the design is fast. In the mixed read-only workload, TIN reported 65 MB read per query versus 582 MB for ParadeDB. Reducing data movement protects the database’s shared I/O and cache budget instead of winning one search benchmark by disturbing every other query.&lt;/p&gt;

&lt;h2&gt;
  
  
  The product boundary matters
&lt;/h2&gt;

&lt;p&gt;TIN is currently a PlanetScale Postgres feature, not a production extension you can install on an arbitrary self-hosted server. PlanetScale provides &lt;a href="https://github.com/planetscale/lead" rel="noopener noreferrer"&gt;Lead&lt;/a&gt;, an open-source compatibility extension for local development and CI. Lead accepts TIN-style SQL but scans table rows; its documentation explicitly says it is unsuitable for production or performance testing.&lt;/p&gt;

&lt;p&gt;That distinction affects architecture decisions. Keeping search inside hosted Postgres removes a separate cluster and synchronization pipeline, but it also couples production behavior to a provider-specific extension. Teams should evaluate both sides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether transactional consistency and operational simplicity outweigh portability;&lt;/li&gt;
&lt;li&gt;whether TINQL and &lt;code&gt;==&amp;gt;&lt;/code&gt; can be isolated behind a repository or query layer;&lt;/li&gt;
&lt;li&gt;how data and queries migrate if the hosted feature no longer fits;&lt;/li&gt;
&lt;li&gt;whether missing features matter, such as stemming, which TIN’s current comparison table marks unsupported;&lt;/li&gt;
&lt;li&gt;how churn, &lt;code&gt;VACUUM&lt;/code&gt;, index high-water growth, and &lt;code&gt;REINDEX&lt;/code&gt; behave on the real dataset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SQL surface makes application migration easier than a proprietary external API would, but it does not make the production engine interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The larger lesson is alignment
&lt;/h2&gt;

&lt;p&gt;TIN’s most transferable idea is not a particular bitmap width or ranking formula. It is to look for a representation that aligns several layers of the system.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;ctid&lt;/code&gt; directly removes ID translation. Splitting it by page and offset makes sparse physical addresses locally dense. Those bitmaps fit vector registers, preserve heap order, intersect naturally with the visibility map, carry liveness state, and keep their meaning across segment merges.&lt;/p&gt;

&lt;p&gt;Many fast systems are built this way. The best optimization is often not a faster implementation of an isolated stage. It is choosing a boundary representation that lets several stages disappear or collapse into the same operation.&lt;/p&gt;

&lt;p&gt;TIN makes full-text search interesting not because it puts a familiar ranking function in Postgres, but because it asks what a search index would look like if it stopped pretending Postgres were somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://planetscale.com/blog/introducing-tin" rel="noopener noreferrer"&gt;PlanetScale, Introducing TIN: full-text search for Postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://planetscale.com/docs/postgres/search" rel="noopener noreferrer"&gt;PlanetScale documentation, TIN: PlanetScale Postgres Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://planetscale.com/docs/postgres/search/get-started" rel="noopener noreferrer"&gt;PlanetScale documentation, Get started with TIN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/planetscale/paradedb-benchmarker" rel="noopener noreferrer"&gt;PlanetScale’s benchmarker fork&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/gin.html" rel="noopener noreferrer"&gt;PostgreSQL documentation, GIN indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/index-scanning.html" rel="noopener noreferrer"&gt;PostgreSQL documentation, Index scanning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/18/storage-vm.html" rel="noopener noreferrer"&gt;PostgreSQL documentation, Visibility map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=49766611" rel="noopener noreferrer"&gt;Hacker News discussion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>software</category>
    </item>
  </channel>
</rss>
