<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Aditi Mishra on Medium]]></title>
        <description><![CDATA[Stories by Aditi Mishra on Medium]]></description>
        <link>https://medium.com/@aditimishra_541?source=rss-f25323722aa0------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*PvF2gFH_e338VybZBDDZOw@2x.jpeg</url>
            <title>Stories by Aditi Mishra on Medium</title>
            <link>https://medium.com/@aditimishra_541?source=rss-f25323722aa0------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 20:29:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@aditimishra_541/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[gRPC, explained like you're calling a function on another computer]]></title>
            <link>https://medium.com/@aditimishra_541/grpc-explained-like-youre-calling-a-function-on-another-computer-c7a74e32b25c?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/c7a74e32b25c</guid>
            <category><![CDATA[grpc]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[backend-development]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 08:51:11 GMT</pubDate>
            <atom:updated>2026-11-11T08:53:05.791Z</atom:updated>
            <content:encoded><![CDATA[<h3>gRPC, explained like you’re calling a function on another computer</h3><p>What it is, how it works under the hood, and when plain REST is still the better choice</p><p>Most services today talk to each other with REST and JSON.</p><p>It works. It is easy to read. You can test it with one curl command.</p><p>But as a system grows, small problems pile up.</p><p>One team renames a field. Another team’s client still sends the old name.</p><p>Every language needs its own hand-written client.</p><p>Nobody is fully sure what the API accepts until something breaks.</p><p>gRPC is one answer to that mess.</p><p>It is an open source RPC framework that started at Google.</p><p>RPC means “remote procedure call.” In plain words: you call a function on another machine.</p><p>Let’s walk through how it works, one piece at a time.</p><h3>Step 1: Write the contract once</h3><p>With gRPC, you start by describing your API in a .proto file.</p><p>It lists the methods, what each one takes in, and what it sends back.</p><p>Here is the small example from the gRPC docs:</p><pre>service HelloService {  rpc SayHello (HelloRequest) returns (HelloResponse);}<br>message HelloRequest {<br>  string greeting = 1;<br>}message HelloResponse {  string reply = 1;}</pre><p>That file is the single source of truth.</p><p>Then a compiler reads it and generates code for you.</p><p>On the server, you get an interface to fill in with your logic.</p><p>On the client, you get a “stub.” A stub is a local object with the same methods as the remote service.</p><p>So calling another service looks like this: client.SayHello(request).</p><p>The generated code packs the request, sends it over the network, and unpacks the reply.</p><p>The client can be in Go and the server in Java. The contract keeps them in sync.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IWXOLAzG0lyoqzi3dydsAA.png" /><figcaption>One .proto file. The compiler generates the client stub and the server interface.</figcaption></figure><h3>Step 2: Protocol Buffers, the message format</h3><p>By default, gRPC sends data as Protocol Buffers, often called protobuf.</p><p>Protobuf is a way to turn structured data into bytes.</p><p>Google describes it as like XML, but smaller, faster, and simpler.</p><p>The key idea is the number next to each field.</p><p>On the wire, a field is sent by its number, not its name.</p><p>That is why the format is compact.</p><p>It is also why you can change your API safely, if you follow two rules.</p><p>Rule one: adding a new field is safe. Old code simply ignores fields it does not know.</p><p>Rule two: never reuse a field number. If you delete a field, mark its number as “reserved” so nobody uses it again.</p><p>The protobuf docs are blunt about this.</p><p>Reusing a number can lead to parse errors, corrupted data, or even leaked personal data.</p><p>A small habit from the same docs: fields 1 to 15 take one byte to tag, so give those numbers to your most used fields.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yCDDNSOOGOmF1WsOUcf5lw.png" /><figcaption>Old code skips fields it does not know. Reusing a number is what breaks things.</figcaption></figure><h3>Step 3: HTTP/2 underneath</h3><p>gRPC runs on top of HTTP/2.</p><p>HTTP/2 is the newer version of the protocol the web runs on.</p><p>Two features matter most here.</p><p>First, many calls can share one connection at the same time. This is called multiplexing.</p><p>So a service that makes hundreds of calls does not need hundreds of connections.</p><p>Second, headers are compressed.</p><p>Repeated headers like auth tokens cost less on every call.</p><h3>Step 4: Four kinds of calls</h3><p>REST is mostly one request, one response.</p><p>gRPC gives you four shapes.</p><p>Unary: one request, one response. Just like a normal function call.</p><p>Server streaming: one request, many responses. Think of a live feed of order updates.</p><p>Client streaming: many requests, one response. Think of uploading many log lines and getting one summary back.</p><p>Bidirectional streaming: both sides send whenever they want. Think of a chat.</p><p>The docs say gRPC keeps messages in order within a single call.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_IcdO3TsLwwoiUM9ChwXHw.png" /><figcaption>The four call shapes gRPC supports.</figcaption></figure><h3>Step 5: Always set a deadline</h3><p>This is the part teams most often skip.</p><p>A deadline is the point in time after which the client stops waiting.</p><p>Here is the surprising default.</p><p>The gRPC docs say that by default, no deadline is set at all.</p><p>That means a client can wait, in their words, “effectively forever.”</p><p>So the advice is simple: always set a realistic deadline on every call.</p><p>When time runs out, the call fails with a status called DEADLINE_EXCEEDED.</p><p>The server side also cancels the call, so it stops wasting work.</p><p>It gets better when one service calls another.</p><p>The docs give an example. A client gives a request 2 seconds.</p><p>The user service spends 0.5 seconds, then calls the billing service.</p><p>gRPC can pass the remaining 1.5 seconds along to billing.</p><p>It sends the time left, not a clock time, so clocks that disagree do not matter.</p><p>Some languages do this by default, like Java and Go. In C++ you have to turn it on.</p><p>One warning from the docs: cancelling a call does not undo changes already made.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZsOq0-2CPrOw2q8ZlpfPmQ.png" /><figcaption>Based on the example in the gRPC deadlines guide. The billing call gets only the time that is left.</figcaption></figure><h3>Step 6: Errors with standard codes</h3><p>In REST, every team invents its own error shapes.</p><p>gRPC has a fixed list of status codes.</p><p>A few you will see often: OK, INVALID_ARGUMENT, NOT_FOUND, DEADLINE_EXCEEDED, and UNAVAILABLE.</p><p>Because the list is shared, retry logic and dashboards can work the same way across every service.</p><h3>The pushback, stated fairly</h3><p>gRPC has real critics. Kevin McDonald wrote a well known post called “gRPC: The Bad Parts,” and it drew over 200 comments on Hacker News.</p><p>Here are the main points, in plain words.</p><p>It is harder to try out. You cannot just send a friend a curl command.</p><p>It adds a build step. Code generation is one more thing to set up, and it feels heavy in dynamic languages.</p><p>Browsers cannot speak plain gRPC. They need gRPC-Web, which often means running a proxy.</p><p>Payloads are binary. You cannot read them in logs without extra tools.</p><p>One top Hacker News comment argued the ecosystem is complex enough that the best supported languages are the ones Google itself maintains.</p><p>Another said most teams do not need gRPC at all, and end up with extra complexity and few real gains.</p><p>To be fair, McDonald also wrote a follow up on the good parts.</p><p>He points out that some of his complaints have since improved, like the standard way to map protobuf to JSON.</p><h3>So when should you use it?</h3><p>Here is a simple rule that holds up well.</p><p>Use gRPC between your own services, where you control both sides.</p><p>There, the shared contract, compact messages, streaming, and deadlines really pay off.</p><p>Use REST or GraphQL at the public edge, where browsers and outside developers call you.</p><p>There, being easy to read and easy to try matters more than saving bytes.</p><p>And whichever you pick, steal one idea from gRPC: put a deadline on every call.</p><p>What does your team use between services today, and what made you choose it?</p><h3>Sources</h3><ul><li>gRPC, Introduction: <a href="https://grpc.io/docs/what-is-grpc/introduction/">https://grpc.io/docs/what-is-grpc/introduction/</a></li><li>gRPC, Core concepts, architecture and lifecycle: <a href="https://grpc.io/docs/what-is-grpc/core-concepts/">https://grpc.io/docs/what-is-grpc/core-concepts/</a></li><li>gRPC, Deadlines: <a href="https://grpc.io/docs/guides/deadlines/">https://grpc.io/docs/guides/deadlines/</a></li><li>gRPC, Status codes: <a href="https://grpc.io/docs/guides/status-codes/">https://grpc.io/docs/guides/status-codes/</a></li><li>Protocol Buffers overview: <a href="https://protobuf.dev/overview/">https://protobuf.dev/overview/</a></li><li>Protocol Buffers, Language Guide (proto3): <a href="https://protobuf.dev/programming-guides/proto3/">https://protobuf.dev/programming-guides/proto3/</a></li><li>RFC 9113, HTTP/2: <a href="https://www.rfc-editor.org/rfc/rfc9113">https://www.rfc-editor.org/rfc/rfc9113</a></li><li>gRPC-Web: <a href="https://github.com/grpc/grpc-web">https://github.com/grpc/grpc-web</a></li><li>Kevin McDonald, “gRPC: The Bad Parts”: <a href="https://kmcd.dev/posts/grpc-the-bad-parts/">https://kmcd.dev/posts/grpc-the-bad-parts/</a></li><li>Hacker News discussion: <a href="https://news.ycombinator.com/item?id=40798740">https://news.ycombinator.com/item?id=40798740</a></li><li>Kevin McDonald, “gRPC: The Good Parts”: https://kmcd.dev/posts/grpc-the-good-parts/</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c7a74e32b25c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What actually happens inside a database when you hit save]]></title>
            <link>https://medium.com/@aditimishra_541/what-actually-happens-inside-a-database-when-you-hit-save-dbc767a1ef77?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/dbc767a1ef77</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[backend-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Mon, 09 Nov 2026 08:44:13 GMT</pubDate>
            <atom:updated>2026-11-09T13:40:27.978Z</atom:updated>
            <content:encoded><![CDATA[<h4>Pages, memory, the write-ahead log, B-trees and LSM trees, MVCC, and the query planner, in plain words</h4><p>You click save.</p><p>A row goes into a table.</p><p>It feels like one step. Inside the database, it is several.</p><p>This piece follows that one write all the way down, using PostgreSQL as the main example, with RocksDB for contrast. Everything here comes from the official docs, linked at the end.</p><h3>Step 1: Data lives in pages, not rows</h3><p>A database does not keep your table as one long list of rows.</p><p>It splits it into pages. A page is a fixed-size block of bytes.</p><p>In Postgres, every table and every index is stored as an array of pages, usually 8 KB each.</p><p>Each page has a small header at the start, 24 bytes.</p><p>Then a list of item pointers. Each one says where a row sits inside the page.</p><p>The rows themselves are packed in from the end of the page, growing backward.</p><p>Free space sits in the middle until the two sides meet.</p><p>Why does this matter? Because the database reads and writes whole pages. Even if you want one row, it fetches the page it lives on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2xeWMV9EdF8dDSfKe-yX6Q.png" /><figcaption>An 8 KB Postgres page: header, item pointers, free space in the middle, rows packed from the end.</figcaption></figure><h3>Step 2: The hot pages stay in memory</h3><p>Reading from disk is slow. Reading from memory is fast.</p><p>So the database keeps a pool of pages in memory. Postgres calls this shared buffers. The general name is the buffer pool.</p><p>Your new row is written into a page in that pool first, not straight to the disk file.</p><p>The default size in Postgres is typically 128 MB. The docs suggest about 25% of RAM as a starting point on a dedicated server with 1 GB or more.</p><p>They also say more than 40% is unlikely to help, because Postgres also relies on the operating system’s own cache.</p><p>A page that has been changed in memory but not yet saved to disk is called dirty.</p><p>A background writer process saves dirty pages to disk over time.</p><h3>Step 3: Write it to the log first</h3><p>Here is the obvious problem. If the change is only in memory and the power goes out, it is gone.</p><p>The fix is the write-ahead log, or WAL.</p><p>The rule is simple. Before a change goes into the data files, a record describing that change must be written to the log and flushed to permanent storage.</p><p>So when you commit, Postgres only has to make sure the log is safely on disk. It does not have to flush every data page you touched.</p><p>If the server crashes, it replays the log and redoes any changes that never made it into the data pages.</p><p>This is also faster, not just safer.</p><p>The log is written in order, one record after another. The docs point out that syncing one sequential file costs much less than flushing many scattered pages.</p><p>And when many small transactions run at once, one flush of the log can commit many of them together.</p><p>The same log powers backups too. Keep the old WAL files, and you can restore a backup and replay up to any point in time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NZ0PyLWHwgVzVGdkw8bCXw.png" /><figcaption>The path of one write: into shared buffers, then the WAL at commit, then data pages later.</figcaption></figure><h3>Step 4: The index has to know about the new row</h3><p>A table with no index is just a pile of pages. To find one row, the database may have to read all of them. That is called a sequential scan.</p><p>An index fixes that. The most common kind is the B-tree.</p><p>A B-tree keeps keys in sorted order, in a tree of pages.</p><p>The top pages point to lower pages. The bottom pages, called leaf pages, point to the actual rows. Postgres says typically over 99% of the pages in a B-tree are leaf pages.</p><p>To find a key, the database walks from the top down. That is a handful of page reads, not the whole table.</p><p>When a leaf page is full, it splits into two. That split can push a new pointer into the parent, and the parent can split too, all the way up.</p><h4>The other design: LSM trees</h4><p>Not every database works this way. RocksDB uses a different structure, the log-structured merge tree, or LSM tree.</p><p>New writes go into an in-memory table called a memtable, and optionally into a write-ahead log.</p><p>When the memtable fills up, it is written to disk as a sorted file. Those files are never changed in place.</p><p>Later, a background job called compaction merges files together and throws away old or deleted values.</p><p>The plain trade-off: writes are cheap because they are batched in memory. Reads can be costlier because a key may be in several files, which is why RocksDB uses tricks like Bloom filters to skip files that cannot hold the key.</p><p>A rough rule many engineers use: read-heavy work leans toward B-trees, write-heavy work leans toward LSM trees. Real systems tune both.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q5OJuVfilKx5YcNXmBtt0Q.png" /><figcaption>B-trees update pages in place. LSM trees flush a memtable to sorted files, then compact.</figcaption></figure><h3>Step 5: Other people are reading at the same time</h3><p>While your write happens, someone else may be reading the same table.</p><p>Postgres handles this with MVCC, short for multiversion concurrency control.</p><p>Each statement sees a snapshot of the data as it was at a point in time.</p><p>So readers never block writers, and writers never block readers.</p><p>The catch is that an UPDATE or DELETE does not remove the old row right away. Another transaction might still need to see it.</p><p>The old versions pile up as dead rows.</p><p>VACUUM is the cleanup job. It marks that space as free for new rows. The autovacuum daemon runs it for you, and it is on by default.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7-l8JjFd41llyXFmc9VMgA.png" /><figcaption>MVCC keeps old and new row versions so readers and writers do not block each other, until VACUUM cleans up.</figcaption></figure><h3>Step 6: Reading it back</h3><p>Later, someone runs a query to find your row.</p><p>The query planner decides how. Use the index? Scan the whole table? Which join method?</p><p>It picks the plan with the lowest estimated cost, based on statistics about your data. The ANALYZE command collects those statistics from a random sample of rows.</p><p>You can see its choice with EXPLAIN. EXPLAIN ANALYZE actually runs the query and shows the real row counts and times next to the estimates.</p><p>The docs say the most important thing to check is whether the estimated row counts are close to reality.</p><h3>The honest catches</h3><p>None of this is free. Each catch below comes straight from the docs.</p><p>An index speeds up reads but adds work to writes. Postgres notes that an UPDATE which cannot use its HOT shortcut needs a new entry in every index on the table, even indexes on columns that did not change.</p><p>The planner can skip your index on purpose. On a tiny table it will nearly always choose a sequential scan, because one page read is cheaper than walking an index.</p><p>Results from a toy table do not carry over to a big one. The docs warn that plan costs are not linear.</p><p>Plain VACUUM does not give space back to the operating system in most cases. It only makes the space reusable. VACUUM FULL shrinks the table but locks it the whole time.</p><p>Long-running transactions hold cleanup back. Old row versions must stay while some transaction might still see them.</p><p>And vacuum is not optional. Postgres transaction IDs are 32 bits. Every table must be vacuumed at least once every two billion transactions, or old data can suddenly look like it is from the future.</p><p>EXPLAIN ANALYZE really runs the statement. If it is an UPDATE or DELETE, the change happens unless you wrap it in a transaction and roll it back.</p><p>LSM trees pay their bill later. RocksDB says overall write speed depends on how fast compaction can keep up, and a burst of writes can stall if the flush threads are busy.</p><h3>What this changes day to day</h3><p>A missing index on a busy column means the database reads pages it did not need to.</p><p>A huge transaction keeps old row versions alive and makes cleanup wait.</p><p>“Just add an index” is a trade, not a free win. Every index makes writes a bit heavier.</p><p>When a query is slow, run EXPLAIN ANALYZE before guessing. Compare the estimated rows to the actual rows.</p><p>And let autovacuum do its job. Turning it off is a decision you will pay for later.</p><p>Which of these steps has bitten you hardest in production: indexes, locks, or cleanup?</p><h3>Sources</h3><ul><li>PostgreSQL, Database Page Layout: <a href="https://www.postgresql.org/docs/current/storage-page-layout.html">https://www.postgresql.org/docs/current/storage-page-layout.html</a></li><li>PostgreSQL, Resource Consumption (shared_buffers, background writer): <a href="https://www.postgresql.org/docs/current/runtime-config-resource.html">https://www.postgresql.org/docs/current/runtime-config-resource.html</a></li><li>PostgreSQL, Write-Ahead Logging (WAL): <a href="https://www.postgresql.org/docs/current/wal-intro.html">https://www.postgresql.org/docs/current/wal-intro.html</a></li><li>PostgreSQL, B-Tree Indexes: <a href="https://www.postgresql.org/docs/current/btree.html">https://www.postgresql.org/docs/current/btree.html</a></li><li>PostgreSQL, Concurrency Control introduction (MVCC): <a href="https://www.postgresql.org/docs/current/mvcc-intro.html">https://www.postgresql.org/docs/current/mvcc-intro.html</a></li><li>PostgreSQL, Routine Vacuuming: <a href="https://www.postgresql.org/docs/current/routine-vacuuming.html">https://www.postgresql.org/docs/current/routine-vacuuming.html</a></li><li>PostgreSQL, Using EXPLAIN: <a href="https://www.postgresql.org/docs/current/using-explain.html">https://www.postgresql.org/docs/current/using-explain.html</a></li><li>RocksDB Wiki, RocksDB Overview: <a href="https://github.com/facebook/rocksdb/wiki/RocksDB-Overview">https://github.com/facebook/rocksdb/wiki/RocksDB-Overview</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dbc767a1ef77" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Load balancers, explained like you’ll actually need them]]></title>
            <link>https://medium.com/@aditimishra_541/load-balancers-explained-like-youll-actually-need-them-15d283aba3c6?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/15d283aba3c6</guid>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[load-balancing]]></category>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[backend-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Sun, 08 Nov 2026 08:37:04 GMT</pubDate>
            <atom:updated>2026-11-09T13:42:50.426Z</atom:updated>
            <content:encoded><![CDATA[<h3>Layer 4 vs Layer 7, the algorithms in plain words, health checks, and how to pick one without overthinking it</h3><p>Almost every system you use today has a load balancer in front of it.</p><p>Most engineers know the word.</p><p>Fewer can say what it actually decides, and why the choice matters.</p><p>This piece is the plain version. Everything here comes from the NGINX, AWS, Envoy, and Cloudflare docs and Google’s Maglev paper, linked at the end.</p><h3>The one-line job</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NSXztv0COLvIoPcNZxsh7A.png" /><figcaption>Clients send requests to a load balancer, which spreads them across healthy servers and skips ones that are down.</figcaption></figure><p>A load balancer has two jobs.</p><p>First, spread requests so no single server drowns.</p><p>Second, stop sending traffic to a server that is down.</p><p>Cloudflare compares it to a grocery store. One open checkout line means a long wait. Open eight lines and the wait drops a lot. The load balancer is the person telling each shopper which line to join.</p><h3>The two big families: Layer 4 and Layer 7</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e0cE4dWWoNWzlURcQQuTOA.png" /><figcaption>Layer 4 sees IPs and ports. Layer 7 reads the HTTP request and can route by path, host, header, or method.</figcaption></figure><p>The “layers” come from the OSI model. That is just a standard way to describe the levels of a network, from wires at the bottom to apps at the top.</p><p>A Layer 4 load balancer works at the transport level.</p><p>It sees IP addresses and ports.</p><p>It does not read the request itself.</p><p>That makes it fast and simple. AWS says its Network Load Balancer, a Layer 4 product, can handle millions of requests per second.</p><p>For TCP, it picks a server using a flow hash. A hash is a function that turns some input into a number. Here the input is the protocol, the source and destination IPs and ports, and the TCP sequence number.</p><p>Each TCP connection then stays on one server for its whole life.</p><p>A Layer 7 load balancer works at the application level.</p><p>It reads the HTTP request: the path, the headers, the cookies, the host name.</p><p>So it can make smarter choices.</p><p>AWS’s Application Load Balancer, a Layer 7 product, can send /api to one group of servers and /images to another. It can route by host name, by header, by query parameter, or by HTTP method.</p><p>The trade is simple.</p><p>Layer 4 is faster and knows less.</p><p>Layer 7 knows more and does more work per request.</p><h3>The algorithms, in plain words</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tqlohKYQ9rfKDoV9dr1hsQ.png" /><figcaption>Round robin goes down the list. Least connections picks the quietest server. Consistent hashing puts servers on a ring so fewer clients move when one joins or leaves.</figcaption></figure><p>Once the load balancer has a list of healthy servers, it needs a rule for picking one.</p><p><strong>Round robin.</strong> Go down the list in order, then start again. It is the default in NGINX and in AWS’s Application Load Balancer.</p><p><strong>Weighted round robin.</strong> Same idea, but some servers get more turns. In the NGINX docs example, one server has weight 3 and two others have the default. Out of every 5 requests, the first gets 3 and the others get 1 each.</p><p><strong>Least connections.</strong> Send the new request to the server with the fewest active connections. NGINX says this is fairer when some requests take much longer than others.</p><p><strong>Power of two choices.</strong> Pick two servers at random and send the request to the less busy one. Envoy uses this for its least request policy. Its docs say research by Mitzenmacher and others shows it is nearly as good as checking every server. It also avoids herding, which is when every load balancer rushes to the same “least busy” server at once.</p><p><strong>IP hash and sticky sessions.</strong> Hash the client’s IP address, so the same client keeps landing on the same server. NGINX says this holds unless that server becomes unavailable. “Sticky” just means the client sticks to one server.</p><p><strong>Consistent hashing.</strong> This one matters when servers come and go.</p><p>With a plain hash, adding one server can reshuffle almost every client.</p><p>Consistent hashing places servers on an imaginary ring. Each request goes to the nearest server on the ring.</p><p>Envoy’s docs say that with this setup, adding or removing one server out of N moves only about 1 in N requests.</p><p>That is a big deal for caches. Less reshuffling means fewer cold caches.</p><h3>Health checks, and what happens when a server dies</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XFGvVHzxNHFK_FZcVc3HeA.png" /><figcaption>Passive checks learn from real failures. Active checks probe on a schedule. Without either, a load balancer may still send traffic to a dead server.</figcaption></figure><p>A load balancer is only as good as its idea of which servers are alive.</p><p>There are two kinds of checks.</p><p><strong>Passive checks</strong> learn from real traffic. Open source NGINX does this. If a request to a server fails, NGINX marks it failed for a while, then slowly tries it again with live requests.</p><p><strong>Active checks</strong> send their own test requests on a schedule. AWS load balancers do this. When a target fails its checks, traffic stops. When it passes again, traffic comes back.</p><p>One detail is easy to miss.</p><p>Envoy’s docs say that if no active health check is set up, it treats every server as healthy unless told otherwise.</p><p>So “we have a load balancer” is not the same as “we route around dead servers.”</p><h3>Where load balancers live</h3><p><strong>Hardware boxes.</strong> Dedicated devices in a data center. Cloudflare’s page says this was the traditional way to do it inside large networks.</p><p><strong>Software.</strong> NGINX, HAProxy, and Envoy run on normal servers, virtual machines, or containers.</p><p><strong>Cloud.</strong> AWS has the Application Load Balancer for Layer 7, the Network Load Balancer for Layer 4, plus Gateway and Classic types. You pay for what you use, and AWS scales the load balancer for you.</p><p><strong>Very large scale.</strong> Google’s Maglev is a software load balancer that runs on ordinary Linux servers. The paper says routers spread packets across many Maglev machines, and each machine then spreads them to the service’s servers. It has served Google’s traffic since 2008, and a single machine could fill a 10Gbps link with small packets.</p><p><strong>DNS and global load balancing.</strong> Your load balancer itself is found through DNS. AWS sets a 60 second time to live on that DNS entry, so the addresses can change quickly. Global server load balancing goes further and sends users to servers in different parts of the world.</p><h3>The trade-offs engineers argue about</h3><p>Load balancing sounds solved. It is not. Here are the honest catches, each straight from the docs.</p><p>Sticky sessions fight even spread. A few heavy clients can pile onto one server.</p><p>Sticky sessions also break when that server goes away. NGINX’s IP hash only keeps you on the same server while it is available.</p><p>Least connections is not always the winner. Envoy prefers power of two choices partly because it resists herding.</p><p>Consistent hashing has its own trade-offs. Envoy says Maglev builds and looks up much faster than ring hash, but about twice as many keys move when servers are removed.</p><p>Passive health checks only learn from failures on real requests. Someone’s request has to fail first.</p><p>DNS-based routing depends on clients behaving. AWS warns that clients which ignore the DNS time to live can keep sending to an address that was removed, and those requests fail.</p><p>Cross-zone settings change the math. In AWS’s own example, with 2 servers in one zone and 8 in another, turning cross-zone off gives each server in the small zone 25% of traffic and each one in the big zone 6.25%. Turned on, all 10 get 10% each.</p><h3>How to choose</h3><p>Start simple.</p><p>Round robin with real health checks covers a lot of systems.</p><p>Use Layer 7 when you need to route by path, host, or header.</p><p>Use Layer 4 when you need raw speed, non-HTTP traffic, or fixed IP addresses.</p><p>Switch to least connections or power of two choices when request times vary a lot.</p><p>Add sticky sessions only when you must.</p><p>The better fix is usually stateless servers, meaning any server can handle any request because the session lives somewhere shared.</p><p>Reach for consistent hashing when servers hold caches or data that you don’t want to reshuffle.</p><p>And always check what your health checks actually test.</p><p>What is one load balancing setting in your system that nobody has looked at in a year?</p><h3>Sources</h3><ul><li>NGINX, Using nginx as HTTP load balancer: <a href="https://nginx.org/en/docs/http/load_balancing.html">https://nginx.org/en/docs/http/load_balancing.html</a></li><li>AWS, How Elastic Load Balancing works: <a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html">https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html</a></li><li>AWS, What is an Application Load Balancer?: <a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html">https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html</a></li><li>AWS, What is a Network Load Balancer?: <a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html">https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html</a></li><li>Envoy, Supported load balancers: <a href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers">https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers</a></li><li>Cloudflare, What is load balancing?: <a href="https://www.cloudflare.com/learning/performance/what-is-load-balancing/">https://www.cloudflare.com/learning/performance/what-is-load-balancing/</a></li><li>Google Research, Maglev: A Fast and Reliable Software Network Load Balancer (NSDI 2016): <a href="https://research.google/pubs/maglev-a-fast-and-reliable-software-network-load-balancer/">https://research.google/pubs/maglev-a-fast-and-reliable-software-network-load-balancer/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15d283aba3c6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SaaS isn’t dead. The cheap version of it just got very cheap.]]></title>
            <link>https://medium.com/@aditimishra_541/saas-isnt-dead-the-cheap-version-of-it-just-got-very-cheap-3e993e60256d?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/3e993e60256d</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[saas]]></category>
            <category><![CDATA[business-intelligence]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Fri, 06 Nov 2026 08:46:45 GMT</pubDate>
            <atom:updated>2026-11-06T08:46:45.493Z</atom:updated>
            <content:encoded><![CDATA[<p>A data team replaced a lot of their $150k Looker use with an afternoon HTML host. Here is what that story actually says about build vs buy.</p><p>In October 2026, David Lequin published a long post with a sharp title: can you replace a $150k Looker with a bucket of HTML files?</p><p>Looker is Google’s business intelligence tool. BI just means software that turns company data into charts and dashboards people can read.</p><p>All the numbers below are Lequin’s, from his post. I have linked it at the end.</p><h3>What his team did</h3><p>His company pays about $150k a year for Looker, roughly 300 seats. That is in line with the average Looker deal he cites.</p><p>They are three data platform engineers at a company of about 500 people. Most of their week is the unglamorous stuff: keeping Snowflake fed, babysitting a dbt project with over 2,000 models, and running the Airflow, Airbyte, DataHub, and Kubernetes plumbing underneath.</p><p>Snowflake is their cloud data warehouse. dbt is how they turn raw tables into clean models. Kubernetes is how they run services. None of that is “an afternoon build.” It was already there.</p><p>In February, a teammate named Maddy vibe-coded a small service in an afternoon. Vibe-coding just means using AI to write most of the code while you steer.</p><p>The service hosted HTML dashboards people were already getting from Claude. Files went into S3. SSO sat in front. There were some tests. Deploying was mostly a Terraform module and a Helm chart on the Kubernetes setup they already ran.</p><p>Eight months later, Lequin says it has about as many weekly users as Looker. Maybe more. Looker usage is down about a third.</p><h3>The part people skip</h3><p>The afternoon build did not invent a data stack.</p><p>It sat on top of one.</p><p>Snowflake already had roles and permissions. dbt already held the models and the meaning of the metrics. The team already knew how to ship a service.</p><p>Lequin is clear about this. When they bought Looker, they were buying four things: a UI to draw charts, a permission model, a semantic layer, and somewhere to host it.</p><p>Claude now draws the charts, one dashboard at a time.</p><p>Snowflake already had the permissions. dbt already had the semantics. Hosting some HTML files, he says, costs about $500 a month.</p><p>Take those away and what is left is a big invoice for the chart layer.</p><h3>What the bill looked like, on his numbers</h3><p>He puts their build cost at about $8.3k over eight months. Most of that is Claude tokens (about $6.4k) plus AWS (about $1.9k).</p><p>Looker’s licence for the same eight months, at the $150k average, would be about $100k.</p><p>Running their tool now costs about $6k a year in AWS, plus Claude spend that has been around $1.3k a month since July. Call it about $20k a year against $150k for Looker.</p><p>He also measured how cheap a single dashboard got. Their analyst agent uses about 1 million tokens per dashboard. After a model upgrade, the cost dropped from about $2.50 to about $0.50.</p><p>His punch line: a BI tool is mostly a way to turn a query into a picture, and a picture now costs about $0.50 of tokens.</p><h3>What it still does not do for you</h3><p>This is where the story stops being a meme and starts being useful.</p><p>Lequin himself says early “chat with your data” demos looked great on a toy schema. The SQL ran without errors. The numbers were wrong. That is much worse than no numbers.</p><p>Their first HTML host also had bad security. Every employee could read every dashboard. Dashboard JavaScript ran in the same place as your session. They knew, and they shipped it anyway, because it was internal, behind VPN and SSO.</p><p>Later they rebuilt the security model properly. Sandboxed iframes. Queries running as the viewer. Checks after publish. Content management that looks like Google Drive.</p><p>Even then, the hard part of BI is not the picture.</p><p>It is agreeing what “revenue” means. It is one definition of a metric, not twelve slightly different ones. It is permissions that match the org chart. It is noticing when a dashboard goes stale and starts lying quietly.</p><p>Lequin’s own line is that the governance that matters already happens upstream, in dbt models and Snowflake roles. That only helps if you already have those upstream layers in good shape.</p><h3>What Hacker News pushed back on</h3><p>The HN thread was useful. The pushback was fair. Here are the main points, each on its own line.</p><p><strong>“This only works because they already had a platform team.”</strong> True. Lequin says the same thing. If you need to hire a team to build this, the maths is different.</p><p><strong>“BI is more than pretty pictures.”</strong> Several commenters said the hard work is deciding what to measure, agreeing on metrics, and enforcing a single source of truth. Making HTML cheaper does not solve that. It can make the mess faster.</p><p><strong>“Wrong numbers are worse than no numbers.”</strong> Lequin already said this about early demos. Commenters repeated it: analysts still matter because dashboards can look confident and still be wrong.</p><p><strong>“The bill leaves out engineer time.”</strong> One commenter argued that once you price the three engineers’ side project properly, the savings look more like maybe half, not twelve times cheaper. Still real. Less magical.</p><p><strong>“Maintenance shows up later.”</strong> Hundreds of one-off dashboards that drift, go stale, or redefine the same metric in slightly different ways. Building is easy. Supporting is harder.</p><h3>What this means if you sell software</h3><p>SaaS is not dead.</p><p>The cheap slice of SaaS just got very cheap.</p><p>If your product is mostly “turn a query into a chart,” and your customer already has a warehouse, a semantic layer, SSO, and a platform team, they can now rebuild that slice with AI for a fraction of the licence.</p><p>If your product is scale that nobody can vibe-code, like storage and compute shared across millions of customers, the story is different. Lequin draws that line himself.</p><p>If your product is the messy middle, permissions, governance, correctness, support, and a shared language for numbers, that middle is still hard. AI makes the pictures cheap. It does not invent agreement.</p><p>I build jewellery software for Indian jewellers with KaratOS. Most of those shops do not have three platform engineers, a Snowflake warehouse, and a dbt project with thousands of models. They will not vibe-code a replacement on a Friday afternoon. They still need something that just works, with support, and without a platform team.</p><p>That is the honest counterpoint.</p><p>Big companies with a data platform can replace the thin chart layer.</p><p>Small businesses without that platform still buy the whole product.</p><h3>The plain takeaway</h3><p>Build vs buy did not disappear.</p><p>The price of building the thin layer collapsed.</p><p>Lequin’s team did not kill BI. They showed that once the warehouse, the models, and the platform plumbing exist, the dashboard UI is no longer worth $150k a year on its own.</p><p>For anyone selling software, the question is blunt.</p><p>Are you selling the thin layer that AI can redraw for fifty cents?</p><p>Or are you selling the hard parts that still take a team, a process, and years of trust?</p><h3>Sources</h3><ul><li>David Lequin, “Can we replace a $150k Looker with a bucket of HTML files?” (Oct 14, 2026): <a href="https://david.lequin.dev/blog/bi-software-is-dead/">https://david.lequin.dev/blog/bi-software-is-dead/</a></li><li>Hacker News discussion: <a href="https://news.ycombinator.com/item?id=50212320">https://news.ycombinator.com/item?id=50212320</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3e993e60256d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI can now prove your code is safe, not just test it]]></title>
            <link>https://medium.com/@aditimishra_541/ai-can-now-prove-your-code-is-safe-not-just-test-it-5e9530faef1f?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/5e9530faef1f</guid>
            <category><![CDATA[security]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[formal-verification]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 08:27:46 GMT</pubDate>
            <atom:updated>2026-11-04T08:27:46.868Z</atom:updated>
            <content:encoded><![CDATA[<p>Anthropic used an AI to write math proofs for 123 open-source libraries. Here is what that means in plain words, and where it stops.</p><p>Testing tells you the code worked for the inputs you tried.</p><p>A proof tells you it works for every input.</p><p>That second thing used to be rare. It was slow, expensive, and needed a very small group of experts.</p><p>On Oct 22, 2026, Anthropic’s Frontier Red Team, working with a company called Theorem, published research saying that is changing. They used their model, Claude, to write proofs for real open-source code at a scale nobody had done before.</p><p>All the numbers below are Anthropic’s, from their post. I have linked it at the end.</p><h3>First, what is formal verification?</h3><p>Formal verification means you turn code into a math model.</p><p>Then you try to prove it always follows a rule you wrote down.</p><p>The rule is called the spec. Spec just means “what correct looks like.”</p><p>If the proof works, the rule holds for every input, not only the ones you tested.</p><p>If the proof fails, the place where it fails often points right at a bug.</p><p>A separate program, called a proof checker, checks the proof line by line. So you do not have to trust whoever wrote the proof. You only have to trust the checker and the rule.</p><h3>Why almost nobody did it</h3><p>Proofs are huge. They are often many times bigger than the code itself.</p><p>They also need a niche skill set.</p><p>Anthropic gives one example. The seL4 operating system kernel took about 2 person-years to build.</p><p>Proving it correct took about 20 person-years.</p><p>So proofs stayed in places where failure is not an option, like the flight software at Airbus.</p><h3>What they actually did</h3><p>They started with fuzz tests.</p><p>A fuzz test throws huge numbers of random, mutated inputs at a piece of code and waits for a crash. Any crash is a bug.</p><p>Google runs this for many open-source projects through a program called OSS-Fuzz.</p><p>The catch is simple. Fuzzing is brute force. It can never try every input.</p><p>So they turned each fuzz test into a rule: “this test never crashes, for any input up to 64 MB.”</p><p>Then they asked Claude to prove that rule.</p><p>Not on the source code. On the compiled machine code for RISC-V, a type of processor design like x86 or ARM.</p><p>Here is what came out, over a few weeks:</p><ul><li>Proofs that 239 test entry points across 123 libraries never crash for inputs up to 64 MB.</li><li>About 180 million lines of proof in total.</li><li>A typical proof ran to about 600,000 lines, for programs with a median size of about 18,000 machine instructions.</li></ul><p>Nobody reads 600,000 lines. That is the point of the proof checker. It reads them instead.</p><h3>The bugs it found along the way</h3><p>Trying to prove “this never crashes” is a great way to find out why it does.</p><p>When Claude got stuck, it was often stuck on a real bug. It patched the bug and kept going.</p><p>Some examples Anthropic gives:</p><ul><li>A counter bug in a decompressor that caused an out-of-bounds write. It only showed up on inputs of around 20 MB with one rare layout.</li><li>An out-of-bounds read in an image decoder. It needed a 64 MB input and over 2 GB of memory.</li><li>A rediscovery of a known security bug in libexpat, a popular XML parser, that needed deeply nested input.</li><li>A file restriction feature in one library that was not always enforced. That one never crashed at all. It was just wrong.</li></ul><p>Most fuzzers use small inputs, often capped at 1 MB. Bugs that need 20 MB of one exact shape are very hard to stumble into by chance.</p><p>Anthropic’s summary line: these bugs survived years of detection “despite trillions of test runs.”</p><h3>It goes beyond “does not crash”</h3><p>This part is easy to miss.</p><p>Many fuzz tests check a real property and crash on purpose if it breaks.</p><p>For example, a compression test can say: compress the input, decompress it, and crash if the result is different.</p><p>So a proof that this test never crashes is a proof that compression always round-trips correctly.</p><p>They also tried other kinds of proofs:</p><ul><li>Claude wrote a formal spec of the zlib compression format and proved the Linux kernel’s zlib code follows it.</li><li>Claude built a Python interpreter in Rust and proved it follows a published description of how Python behaves. Across 1.7 million random Python programs, it matched real Python in 99.95% of them.</li><li>While trying to verify a widely used PDF reader, they found as many security bugs in one week as are usually reported in a year.</li></ul><h3>What Anthropic says it does not prove</h3><p>Their own limits section is worth reading. In short:</p><p>“Formally verified” does not mean “bug free.”</p><p>The proofs assume a single-threaded program on RISC-V, linked a certain way. Most real users run x86 or ARM with a different setup.</p><p>They did not verify every entry point, and entry points do not cover a whole library.</p><p>The memory checker they relied on does not catch every memory bug.</p><p>A program that calls the library the wrong way can still break things.</p><p>Cost is real too. Proofs can take hundreds of millions of tokens. Tokens are the small chunks of text a model reads and writes, and you pay for them.</p><p>They are publishing the trusted parts and a subset of the proofs so anyone can check them.</p><h3>What the critics said</h3><p>The Hacker News thread passed 470 points and 300 comments. The pushback was sharp and fair.</p><p><strong>“Verified against what?”</strong> A proof is only as good as the rule you wrote. If the rule is wrong, a perfect proof proves the wrong thing.</p><p><strong>“Can the AI cheat?”</strong> One developer said that when they use AI with proof tools, it tries to remove the checks instead of passing them. Anthropic admits this risk. They call it reward hacking: the model could make a rule easy to prove by changing the program. They say they penalized patches and audited them.</p><p><strong>“The proofs are bloated.”</strong> Anthropic’s own appendix says the AI writes very long, repetitive proofs where a human expert would build cleaner building blocks. A commenter quoted it and asked if this style can scale to harder programs.</p><p><strong>“Normal teams will not bother.”</strong> One commenter expects proofs to stay in already careful fields like cryptography and aerospace, while everyone else ships faster instead.</p><p>There was support too.</p><p>Several people pointed out that this is strictly stronger than fuzzing. A fuzzer samples inputs. A proof covers all of them, up to the limit.</p><p>And one engineer said they already do something like this at work: an AI opens a speed-up change with a proof that it behaves the same as the old code.</p><h3>Where this fits first</h3><p>You will not prove your whole app next week.</p><p>But some code is small, critical, and has a clear rule:</p><ul><li>Parsers and decoders that read untrusted files.</li><li>Compression code.</li><li>Crypto code.</li><li>Payment and money math.</li><li>Shared libraries that thousands of other programs depend on.</li></ul><p>That is where “prove it” starts to make sense.</p><p>Anthropic also argues proofs can be reused. When agents could copy each other’s finished proofs, they needed 17% fewer tokens. Prove a popular library once, and everything on top of it benefits.</p><h3>The plain takeaway</h3><p>Testing asks: did it break on what I tried?</p><p>Proving asks: can it ever break, under these rules?</p><p>AI just made the second question much cheaper to ask.</p><p>It did not make writing the rules any easier. That part is still on us.</p><h3>Sources</h3><ul><li>Anthropic, “LLMs are making formal software verification feasible at a much larger scale” (Oct 22, 2026): <a href="https://www.anthropic.com/research/formal-verification">https://www.anthropic.com/research/formal-verification</a></li><li>Hacker News discussion: <a href="https://news.ycombinator.com/item?id=50202217">https://news.ycombinator.com/item?id=50202217</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5e9530faef1f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The code on GitHub was clean. The package you installed wasn’t.]]></title>
            <link>https://medium.com/@aditimishra_541/the-code-on-github-was-clean-the-package-you-installed-wasnt-8043a6461f45?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/8043a6461f45</guid>
            <category><![CDATA[supply-chain-security]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 08:29:59 GMT</pubDate>
            <atom:updated>2026-11-02T08:29:59.250Z</atom:updated>
            <content:encoded><![CDATA[<p>What the LiteLLM 1.107.0 attack teaches about trusting AI dependencies</p><p>On October 21, 2026, a bad version of LiteLLM went up on PyPI.</p><p>LiteLLM is a popular Python package. It lets one app talk to many AI model providers through one interface.</p><p>PyPI is the public store where pip install gets Python packages.</p><p>Version 1.107.0 carried a credential stealer and a backdoor.</p><p>The strange part: the code on GitHub was clean.</p><p>If you read the source, you would have found nothing.</p><p>That is the lesson worth sitting with.</p><h3>What happened, in plain words</h3><p>The bad version was live on PyPI for about two hours, from 11:39 to 13:23 UTC.</p><p>Some Docker images were bad for a bit longer, until 14:20 UTC.</p><p>Docker images are ready-to-run packaged copies of the app.</p><p>All seven wheels were affected. A wheel is a prebuilt package for one type of computer. That covered Linux, macOS and Windows.</p><p>LiteLLM pulled the release the same day. Versions 1.106.2 and the 1.107.0 release candidates are clean, per SafeDep’s analysis.</p><h3>How the malware ran without you even using it</h3><p>This is the clever part.</p><p>The package dropped three extra files into site-packages. That is the folder where Python keeps installed packages.</p><p>One file was cook.pth.</p><p>A .pth file is a small config file Python reads every time it starts. If a line in it starts with import, Python runs it.</p><p>So the stealer ran in every Python process on the machine.</p><p>Your app. pip itself. Your editor’s language tools. Your CI jobs.</p><p>You did not need to import LiteLLM at all.</p><p>The second file, sitecustomize.py, also runs at Python startup. It started a hidden background process that checked in every hour for new code to run.</p><h3>What it tried to steal</h3><p>SafeDep decoded the stealer. The list is long:</p><p>SSH keys.</p><p>Cloud credentials for AWS, Azure and Google Cloud.</p><p>Kubernetes and Docker config.</p><p>CI/CD secrets.</p><p>Saved browser passwords.</p><p>Tokens for AI coding tools like Claude Code, Cursor, Copilot and others.</p><p>And the LLM provider API keys stored inside a LiteLLM proxy database.</p><p>That last one matters. A tool whose whole job is to hold your AI keys is a very good place to steal them from.</p><h3>How a clean repo shipped a dirty package</h3><p>The only visible change in the source bundle was one extra line in pyproject.toml, the file that tells Python how to build the package.</p><p>The line was data = &quot;cook&quot;.</p><p>It told the build tool to copy a cook folder into the package. That folder was not in any public commit.</p><p>So where did it come from?</p><p>LiteLLM’s own incident report explains the chain.</p><p>The attacker had GitHub CLI login tokens for three LiteLLM developers.</p><p>LiteLLM believes these tokens leaked into the logs of an internal LiteLLM deployment that was reachable from the internet. Their report says Claude Code can, in some cases, put a GitHub CLI token into a response body, and those bodies were being logged.</p><p>One token belonged to a GitHub organization owner.</p><p>The attacker used it to turn off branch protection and change the private release workflow.</p><p>Then they pointed that workflow at a lookalike repository: Berri-AI/litellm instead of BerriAI/litellm.</p><p>A second developer’s token passed the approval gate.</p><p>The official pipeline then built, signed and published the poisoned files.</p><p>A third developer’s token was used to delete the workflow records.</p><h3>Why the usual checks didn’t save anyone</h3><p>This is the uncomfortable part.</p><p>After an earlier incident in March, LiteLLM had hardened its pipeline. Private release repo. Trusted Publishing. Signed Docker images.</p><p>Trusted Publishing is a PyPI feature that proves a package came from a specific CI workflow, not from someone’s laptop.</p><p>But here the attacker used the real workflow.</p><p>So the package was signed by the real key. It came from the real pipeline.</p><p>One HN commenter put it well: attestations prove which workflow published a package. They don’t prove what source that workflow checked out.</p><p>What would have caught it? Rebuilding from the public tag and comparing the result. Almost nobody does that for Python packages.</p><h3>What teams can actually do</h3><p>None of this is exotic. It is just discipline.</p><p>Pin exact versions. Don’t let latest decide what runs in production.</p><p>Use hash-locked dependencies, so a package can’t change under the same version number.</p><p>Add a cooldown. Don’t install a release on day one. This one was live for about two hours.</p><p>Watch for new .pth files and sitecustomize.py in your environments.</p><p>Keep secrets out of logs. Scan logs for tokens.</p><p>Give the tool less. A proxy that only talks to your model provider can’t send your keys anywhere else.</p><p>If you installed 1.107.0, assume your secrets are gone. LiteLLM’s report says to clean the files from the shell first, because running pip starts Python and runs the malware again. Then rotate every key the machine could reach.</p><h3>The pushback, fairly</h3><p>The HN thread had over 200 comments. Here are the main arguments.</p><p>“Cooldowns are snake oil.” Some argue real attacks hide for months, so a one or two day wait does little.</p><p>Others pushed back with data. They said most major package attacks are caught within about a day, so a short wait does help.</p><p>“Use a sandbox.” Several people said to run tools like LiteLLM with no access to your home folder and only the network hosts they need.</p><p>“Then the sandbox gets hacked.” Someone replied that you still have to trust something. The reply: layer two unrelated tools, so one compromise isn’t enough.</p><p>“This is the second time this year.” LiteLLM’s own report mentions the March incident. Many commenters said they are moving away.</p><p>“Just write your own.” Some said a small proxy is easy now with AI help. Others said they’d rather not maintain every provider’s quirks themselves.</p><p>“It wasn’t simple negligence.” LiteLLM’s CEO listed several things that had to line up, including the token-logging behavior they say they didn’t know about.</p><p>“It was still negligence.” Critics said an internet-facing deployment with logged tokens, plus an org owner token, is a governance problem, not bad luck.</p><p>I don’t think we need to pick a villain here.</p><p>The facts are enough.</p><h3>The real lesson</h3><p>We tend to trust what we can read.</p><p>The source on GitHub. The signature. The green check.</p><p>This attack passed all of them.</p><p>The code you review and the package you install are two different things. Something builds one from the other, and that something can be fooled.</p><p>AI stacks make this worse. They pull in fast-moving packages. And those packages hold the most valuable keys you have.</p><p>So treat your AI dependencies like production infrastructure.</p><p>Because that is what they are.</p><h3>Sources</h3><ul><li>SafeDep, “Compromised LiteLLM 1.107.0 on PyPI Installs a Credential Stealer and Backdoor”: <a href="https://safedep.io/litellm-pypi-compromise-credential-stealer-backdoor/">https://safedep.io/litellm-pypi-compromise-credential-stealer-backdoor/</a></li><li>LiteLLM incident report, “Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised”: <a href="https://docs.litellm.ai/blog/security-incident-oct-21-2026">https://docs.litellm.ai/blog/security-incident-oct-21-2026</a></li><li>Hacker News discussion: <a href="https://news.ycombinator.com/item?id=50181114">https://news.ycombinator.com/item?id=50181114</a></li><li>LiteLLM GitHub issue: <a href="https://github.com/BerriAI/litellm/issues/47826">https://github.com/BerriAI/litellm/issues/47826</a></li><li>PyPI Trusted Publishers docs: <a href="https://docs.pypi.org/trusted-publishers/">https://docs.pypi.org/trusted-publishers/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8043a6461f45" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI can port a compiler in days. Keeping it correct is the real work.]]></title>
            <link>https://medium.com/@aditimishra_541/ai-can-port-a-compiler-in-days-keeping-it-correct-is-the-real-work-c0a2ffc9e3fd?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/c0a2ffc9e3fd</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[rust]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Sun, 01 Nov 2026 08:36:51 GMT</pubDate>
            <atom:updated>2026-11-01T08:36:51.571Z</atom:updated>
            <content:encoded><![CDATA[<p>What Cloudflare’s Rust port of TypeScript teaches any team using AI for a big migration.</p><p>On October 20, 2026, Cloudflare and VoidZero announced tsrs.</p><p>It’s a port of the TypeScript compiler to Rust.</p><p>A <strong>port</strong> means rewriting the same program in a different language, keeping its behavior the same.</p><p>The headline was speed. The more useful part was a few paragraphs further down.</p><p>They explained how they plan to keep the port correct, every day, for years.</p><p>That part applies to anyone using AI to move code from one place to another.</p><h3>What they announced</h3><p>tsrs is being built with Max Schwenk of Owner.com, in close collaboration with the TypeScript team.</p><p>It lives inside Oxc, a set of fast JavaScript tools written in Rust.</p><p>They were clear about one thing: this is not a fork.</p><p>A <strong>fork</strong> is a copy that goes its own way. A port tries to stay identical to the original.</p><p>In their words, the TypeScript team “should remain solely responsible” for what the language looks like.</p><p>You can try an experimental build today with npm install tsrs.</p><h3>The speed claims, labeled honestly</h3><p>Cloudflare says tsrs is up to 8x faster than TypeScript 7.0.</p><p>They also say it uses about 70% less memory, with the same number of type check workers.</p><p>These are Cloudflare’s own benchmarks. Treat them as a starting point, not a verdict.</p><p>To their credit, they said they worked with the TypeScript team to compare the same settings.</p><p>They also called out a common trick: other ports often look fast because they use bigger machines or different options.</p><p>And the official TypeScript is getting faster too. Their chart shows TypeScript 7.1 beta up to 2.5x faster than 7.0.</p><p>Some of those gains came from ideas found while building tsrs.</p><h3>Why this was possible now</h3><p>Porting TypeScript is hard. Over the years, many people tried.</p><p>Cloudflare says only one port really succeeded before: the TypeScript team’s own move from JavaScript to Go.</p><p>Their explanation for what changed is short.</p><p>“Frontier AI models can now do large-scale ports almost independently in a matter of days.”</p><p>They also admit the downside. The internet now has “slopforks,” quick AI ports that don’t hold up.</p><p>So the hard question is no longer “can we port it?”</p><p>It’s “can we keep it right?”</p><h3>Why Rust, when Go already exists</h3><p>As Cloudflare notes, TypeScript’s Go version already made it about 10x faster than the old one.</p><p>So why another language?</p><p>The answer is <strong>WebAssembly</strong>, or Wasm. It’s a format that lets code run safely inside a browser or a sandbox.</p><p>When you compile Go to Wasm, you have to ship Go’s garbage collector with it.</p><p>A <strong>garbage collector</strong> is the part of a language that cleans up memory you no longer use. It adds size and overhead.</p><p>Rust doesn’t need one. That makes it lighter in Wasm.</p><p>For Cloudflare, that matters. They want to run full development tools inside small sandboxes, often for AI agents.</p><p>Smaller memory means more of these can run, cheaper, and in more places.</p><h3>The real story: how they keep the port faithful</h3><p>Here’s the process they described, borrowed from Vinext, their port of Next.js.</p><p>Each morning, an agent reads the new changes in the original project.</p><p>It fetches the diffs and opens tracking issues for anything that could matter.</p><p>Every night, they run the original project’s test suite against the port.</p><p>That builds a <strong>compatibility matrix</strong>, a table of what passes and what doesn’t.</p><p>When a test fails, agents find the matching change in both codebases.</p><p>They build a small repro, port the relevant tests, and propose a fix.</p><p>They say they will apply the same process to TypeScript, “taking extreme care” not to drift from the original.</p><p>And when they find a speedup, they plan to send it back to the official Go version.</p><h3>The small differences they admit</h3><p>They didn’t claim the port is perfect.</p><p>They named a real gap: the order in which errors are printed can differ from the original.</p><p>They aim to shrink those gaps before a stable release.</p><p>This is the honest part of any port. Small differences are where users get surprised.</p><h3>What the pushback said</h3><p>The Hacker News thread had over 200 comments. Here are the main points, fairly.</p><p>“Up to 8x faster at what?” Some readers doubted the numbers. That’s fair. Test on your own code.</p><p>“Is the TypeScript compiler really that slow?” Others said yes. One reader said it’s too slow to run locally on their large codebase.</p><p>“You probably don’t need to switch.” One commenter noted that Cloudflare runs huge numbers of these, so its needs differ from a normal web developer’s.</p><p>“The speed isn’t mostly about Rust.” A TypeScript team member (posting as DanRosenwasser) said they did their own Rust vibe port. They suspect much of the speed comes from optimizations, not the language.</p><p>“Ported code keeps the old design.” One reader said AI ports copy the original structure, built for a different language, instead of designing for Rust.</p><p>“Is the memory trick safe?” One reader looked at the porting notes and argued that a shortcut used to fake a garbage collector hides real risk. That’s one reader’s view, not a settled finding.</p><p>“Who reviews this code?” Another reader worried that agents write code in ways no human expects, so it may break in ways no human wrote a test for.</p><p>There was also real praise. One reader who benchmarked several tools found tsrs very memory efficient with more threads, and liked that it supports incremental checks.</p><h3>Lessons for any team using AI for a migration</h3><p>You don’t need to be porting a compiler for this to matter.</p><p>Here’s what carries over.</p><p><strong>The test suite is the spec.</strong> If the original has good tests, the port can be checked. If not, write them first.</p><p><strong>Track the original every day.</strong> A port that stops following its source starts to drift.</p><p><strong>Measure on the same setup.</strong> Same machine, same options. Otherwise the speed numbers mean little.</p><p><strong>Expect small differences.</strong> Error order, edge cases, timing. List them. Shrink them on purpose.</p><p><strong>Send improvements back.</strong> The best ports make the original better too, as TypeScript 7.1 shows.</p><p><strong>Keep humans on the hard parts.</strong> Reviews of memory safety and design choices still need people who understand them.</p><h3>The takeaway</h3><p>AI made the first version of a port cheap.</p><p>It did not make correctness cheap.</p><p>The teams that win will be the ones with the best tests and the most boring daily routine for staying in sync.</p><p>The writing got faster. The checking is now the job.</p><h3>Sources</h3><ul><li>Cloudflare blog, “Introducing tsrs: A port of TypeScript to Rust” (Oct 20, 2026): <a href="https://blog.cloudflare.com/typescript-rust-port/">https://blog.cloudflare.com/typescript-rust-port/</a></li><li>Hacker News discussion, “Tsrs: A Port of TypeScript to Rust”: <a href="https://news.ycombinator.com/item?id=50165953">https://news.ycombinator.com/item?id=50165953</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c0a2ffc9e3fd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why the AI remembers your chat: the KV cache, explained simply]]></title>
            <link>https://medium.com/@aditimishra_541/why-the-ai-remembers-your-chat-the-kv-cache-explained-simply-1d6586f5760b?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/1d6586f5760b</guid>
            <category><![CDATA[large-language-models]]></category>
            <category><![CDATA[llm-inference]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Fri, 30 Oct 2026 08:28:41 GMT</pubDate>
            <atom:updated>2026-10-30T08:29:45.129Z</atom:updated>
            <content:encoded><![CDATA[<p>The hidden memory that makes chatbots fast, why it eats so much GPU memory, and the operating system trick that fixed the waste.</p><p>You type a question. The answer streams back one word at a time.</p><p>Behind that stream is a simple trick called the KV cache.</p><p>It’s the reason answers come back fast. It’s also one reason long chats cost more and context limits exist.</p><p>Here’s how it works, in plain words, with real numbers from the paper that changed how most of the industry handles it.</p><h3>The model writes one token at a time</h3><p>A <strong>token</strong> is a small piece of text. Often a word, sometimes part of a word.</p><p>A language model writes its answer one token at a time.</p><p>To pick the next token, it looks back at every token before it. Your prompt, plus everything it has written so far.</p><p>That “looking back” is <strong>attention</strong>, the part of a transformer that lets each token check the others.</p><h3>Keys and values</h3><p>For every token, each attention layer makes three lists of numbers.</p><p>A <strong>query</strong>: what this token is looking for.</p><p>A <strong>key</strong>: what this token can be matched on.</p><p>A <strong>value</strong>: what this token passes on when it matches.</p><p>The new token’s query is compared with the keys of all earlier tokens. The best matches decide which values get mixed into the answer.</p><p>If you know backend caches, this will feel familiar.</p><h3>Why a cache helps</h3><p>Here’s the key point.</p><p>Once a token is written, its key and value never change.</p><p>So there’s no reason to compute them again.</p><p>Without a cache, the model would redo that work for every earlier token at every step. Token 500 would recompute the keys and values for tokens 1 to 499.</p><p>With a cache, it stores each token’s key and value once. At each new step it only computes the new token’s key and value, then reads the rest from memory.</p><p>That stored pile of keys and values is the <strong>KV cache</strong>.</p><h3>Two phases: reading and writing</h3><p>The vLLM paper splits the work into two phases.</p><p><strong>The prompt phase.</strong> The model reads your whole prompt at once. All those tokens are known, so the GPU can work on them in parallel. This part is efficient.</p><p><strong>The generation phase.</strong> The model writes one token at a time. Each step depends on the one before, so it can’t run in parallel.</p><p>The paper says this second phase underuses the GPU and is “memory-bound.” In plain words, the GPU spends its time waiting on memory, not doing math.</p><p>This phase takes most of the time for a single request.</p><h3>The cache is big</h3><p>The paper gives a worked example for OPT-13B, a 13 billion parameter model.</p><p>One token’s cache takes about 800 KB.</p><p>That’s 2 (key and value) times 5,120 numbers per layer, times 40 layers, times 2 bytes per number.</p><p>OPT can handle up to 2,048 tokens. So one request’s cache can need up to 1.6 GB.</p><p>On a 40 GB NVIDIA A100 GPU serving that model, about 65% of memory holds the model weights. Close to 30% holds the KV cache.</p><p>The weights stay the same for every user. The cache is different for every request.</p><p>So how you manage the cache decides how many requests can run at once.</p><h3>The waste problem</h3><p>Older serving systems kept each request’s cache in one unbroken chunk of memory.</p><p>But nobody knows ahead of time how long an answer will be.</p><p>So they reserved space for the longest possible answer, like 2,048 tokens. Even when the real answer was much shorter.</p><p>That wasted memory in three ways:</p><ul><li><strong>Reserved space</strong> held for tokens not written yet.</li><li><strong>Internal fragmentation</strong>: space inside a chunk that never gets used, because the answer ended early.</li><li><strong>External fragmentation</strong>: small gaps between chunks too small to use.</li></ul><p>The paper measured it. In the systems it tested, only 20.4% to 38.2% of the KV cache memory held real token data.</p><p>The rest was waiting or wasted.</p><h3>The fix: borrow paging from operating systems</h3><p>The vLLM team, mostly from UC Berkeley, borrowed an old idea: virtual memory with paging.</p><p>Your operating system doesn’t give a program one big unbroken block of RAM. It splits memory into small fixed-size <strong>pages</strong>. A lookup table maps the program’s view of memory to wherever those pages really sit.</p><p><strong>PagedAttention</strong> does the same for the KV cache.</p><p>It splits each request’s cache into small <strong>blocks</strong>. Each block holds the keys and values for a fixed number of tokens. vLLM’s default is 16 tokens per block.</p><p>Blocks don’t need to sit next to each other. A block table tracks where each one lives.</p><p>New blocks are handed out only when they’re needed.</p><p>The paper maps it out directly: blocks are like pages, tokens are like bytes, and requests are like processes.</p><p>The result: waste is limited to the last, partly filled block of each request. In the paper’s chart, vLLM used 96.3% of its KV cache memory for real tokens.</p><h3>Sharing, too</h3><p>Paging also makes sharing easy.</p><p>Sometimes many outputs start from the same prompt. For example, asking for several code suggestions at once. Their prompt blocks can be shared instead of copied.</p><p>When one output needs to change a shared block, vLLM copies it only then. That’s called <strong>copy-on-write</strong>, another trick borrowed from operating systems.</p><p>The paper reports beam search, a way of exploring several candidate answers at once, can save up to 55% of memory this way.</p><p>The same idea covers a shared <strong>system prompt</strong>, the fixed instructions placed before every user’s message. Its cache can be computed once and reused.</p><h3>What it bought</h3><p>Less waste means more requests fit in memory at once. More requests in a batch means more work done per second.</p><p>The paper reports vLLM improved throughput by 2 to 4 times over the systems it compared against, FasterTransformer and Orca, at the same level of latency.</p><p>vLLM is now a popular open source serving engine. Its GitHub page describes it as “a high-throughput and memory-efficient inference and serving engine for LLMs.”</p><h3>The pushback</h3><p>The 2023 Hacker News thread was mostly positive. But a few fair points came up.</p><p><strong>“It helps most when the GPU is full.”</strong> One commenter suspected much of the speedup comes from saving memory on a GPU that’s nearly full. A vLLM author agreed it focuses on maximizing throughput when GPU memory is fully used, and said other optimizations still help otherwise.</p><p><strong>“It won’t let you run bigger models on small hardware.”</strong> Asked about running huge models on a laptop, an author said vLLM raises throughput but doesn’t lower the minimum resources needed to run a model.</p><p><strong>“How is this different from FlashAttention?”</strong> Several people, including an author, said the two are separate ideas. FlashAttention cuts memory reads and writes. PagedAttention cuts memory waste.</p><p><strong>“Paging isn’t new.”</strong> One commenter joked about PhDs discovering parallelization and paging. That’s true, and the paper says so itself. The idea is old. Applying it here was the new part.</p><h3>What this means if you use AI tools</h3><p><strong>Long chats get heavier.</strong> Every token in the conversation has a cache entry. More context means more memory per request.</p><p><strong>Context limits are partly a memory limit.</strong> The cache grows with every token, so it can’t grow forever.</p><p><strong>A shared starting prompt can be cheap.</strong> If the system prompt is the same, its cache can be reused instead of rebuilt.</p><p><strong>Answers stream because of how generation works.</strong> One token at a time, each one reading the cache.</p><h3>What I think</h3><p>I like this paper because the hardest AI problem in it is a plain systems problem.</p><p>Memory allocation. Fragmentation. Paging. Copy-on-write.</p><p>The lesson I take from it: old operating system ideas still have a lot to give to AI infrastructure.</p><p>What old engineering idea do you think AI systems will borrow next?</p><h3>Sources</h3><ul><li>Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention” (SOSP 2023): <a href="https://arxiv.org/abs/2309.06180">https://arxiv.org/abs/2309.06180</a></li><li>vLLM on GitHub: <a href="https://github.com/vllm-project/vllm">https://github.com/vllm-project/vllm</a></li><li>Hacker News discussion of vLLM (2023): <a href="https://news.ycombinator.com/item?id=36409082">https://news.ycombinator.com/item?id=36409082</a></li><li>Vaswani et al., “Attention Is All You Need”: <a href="https://arxiv.org/abs/1706.03762">https://arxiv.org/abs/1706.03762</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d6586f5760b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You Can Outsource the Typing, Not the Understanding]]></title>
            <link>https://medium.com/@aditimishra_541/you-can-outsource-the-typing-not-the-understanding-81d85f93184e?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/81d85f93184e</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[developer-tools]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[code-review]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Wed, 28 Oct 2026 08:32:38 GMT</pubDate>
            <atom:updated>2026-10-28T08:32:38.869Z</atom:updated>
            <content:encoded><![CDATA[<p>When AI writes the code and AI reviews it, someone still has to know what ships. Three ideas from this month, and the honest pushback.</p><p>Yuchen Jin, who builds AI systems at Databricks, posted a joke on X this month.</p><p>It went like this.</p><p>Engineer 1 asks the AI: “Build this and open a PR.”</p><p>Engineer 1 asks Engineer 2: “Can you review?”</p><p>Engineer 2 asks the AI: “Review this PR.”</p><p>Engineer 2 tells Engineer 1: “LGTM.” (Looks good to me.)</p><p>His punchline: both engineers use the exact same AI model.</p><p>It got over 150,000 views and about 1,000 likes. People laughed because it’s close to true.</p><p>The joke hides a real question.</p><p>If an agent writes the code, and an agent reviews it, who on the team actually understands what ships?</p><h3>Why this matters</h3><p>Typing code was never the hard part.</p><p>The hard part is the picture in your head. How the pieces fit. What breaks when the network times out. Why that odd line is there.</p><p>One Hacker News commenter put it well. Typing is a small part of the work. Building the model in your head is the bottleneck.</p><p>AI is very good at the typing.</p><p>It can’t hold the picture for you. When something breaks at 2am, the picture has to be in a human head.</p><p>This month brought three different answers to this problem. One is a habit. One is a tool. One is a warning.</p><h3>Answer 1: the Brain Sandwich</h3><p>The first is a simple habit from the Terrible Software blog.</p><p>The author describes a 1:1 with Emily, one of their strongest engineers. She uses a framework she calls the Brain Sandwich.</p><p>It has three layers.</p><p>Your brain. Then AI. Then your brain again.</p><p><strong>Brain first.</strong> Before you ask the agent to build anything, read the code and understand the problem. You don’t need a full plan. You need a rough idea of where you’re going.</p><p>Why? Without it, the AI can push you the wrong way. Or it can help you build something that wasn’t a problem at all.</p><p><strong>AI in the middle.</strong> Now hand the work over. Fully. No shame.</p><p>Because you did the first step, you can tell when the result is right, too big, or solving the wrong thing.</p><p><strong>Brain last.</strong> Before anyone else reviews it, make it reviewable. Read the diff (the list of changed lines). Ask if the complexity is worth it. Write a real PR description.</p><p>The rule of thumb from the post:</p><p>If you can’t explain what’s changing and why, it’s not ready for someone else to look at.</p><h3>Answer 2: Sphinx, a quiz before you push</h3><p>The second answer is a tool.</p><p>Sphinx is a free, open source Git hook. A hook is a small script Git runs at certain moments, like right before a push.</p><p>Its Show HN post got about 250 points and close to 90 comments.</p><p>Here’s how it works.</p><p>You push code. Or your agent does.</p><p>On branches you protect, Git pauses.</p><p>A separate AI reviewer, one that never saw your chat with the coding agent, reads the diff. Then it asks you a question about what the change does when things go wrong.</p><p>Their site shows a made-up example.</p><p>A payment webhook (a URL that another service calls to tell you something happened) now saves the event ID before it charges the customer.</p><p>The question: Stripe times out and sends the same event again. What happens on the retry?</p><p>The first answer is wrong. “It charges twice.”</p><p>Sphinx explains why. The retry finds the saved ID and skips the charge.</p><p>Then it asks the harder follow-up. The ID is saved before the charge succeeds. So if the charge fails, what does the next retry do?</p><p>Right answer: it skips the charge. The customer is never billed.</p><p>That’s a real bug. And it’s exactly the kind a quick “LGTM” misses.</p><p>A few other details from the site.</p><p>It works with Codex, Claude Code, Cursor, OpenCode, Pi, or plain Git.</p><p>Your answers never go back to the coding agent.</p><p>It doesn’t open .env files or keys.</p><p>And there’s an escape hatch. A command lets the push through when you’re in a hurry, and every skip is logged.</p><h3>Answer 3: the warning from mathematics</h3><p>The third answer comes from far outside software.</p><p>Francesco Maggi wrote a guest post on Terence Tao’s blog about AI in mathematics.</p><p>AI tools now produce very long proofs. Some are so long that people can’t read them directly.</p><p>He describes two futures. A golden age, where AI helps people understand more. And a dark age, where output keeps growing but human understanding gets weaker.</p><p>His line: “The real darkness here would be knowledge becoming alien to human understanding.”</p><p>He also asks a question every engineering team should steal.</p><p>How do we use these tools to amplify our abilities, without handing off the very work that builds expertise in the first place?</p><p>Swap “mathematician” for “engineer” and it fits our world perfectly.</p><h3>The pushback, stated fairly</h3><p>Both Hacker News threads had strong pushback. A lot of it is fair.</p><p><strong>“People will just skip it.”</strong> One commenter said 1 in 100 people would actually do the brain-first step.</p><p><strong>“They’ll paste the quiz into the AI.”</strong> Nothing stops a lazy person from doing that.</p><p><strong>“They’ll remove the hook.”</strong> One team lead said their developers already ask agents to delete Git hooks they don’t like.</p><p><strong>“A quiz checks the person, not the code.”</strong> You can pass the quiz and still ship a leftover debug print or a broken edge case.</p><p><strong>“Knowing what it does is not knowing if it’s wrong.”</strong> One commenter said the real value of review is spotting the bad assumption.</p><p><strong>“It doesn’t fix review volume.”</strong> You still have more AI code to review than before.</p><p><strong>“Management may not care.”</strong> If speed is all that counts, careful work can look like slow work.</p><p><strong>“Reading first is slow.”</strong> One senior engineer said it’s faster to let the AI explore, then question it hard before it builds.</p><p><strong>“Write a better PR description instead.”</strong> Explaining why, what, and how forces you to understand the change.</p><p>My take: most of this is right.</p><p>A quiz is a floor, not a ceiling. It doesn’t replace tests or real review.</p><p>Sphinx itself shows it has to bend: the logged skip is there for a reason. If the skips pile up, you learn something about your team.</p><h3>A simple checklist for your team</h3><p>Here’s what I’d try.</p><p><strong>Protect only what’s risky.</strong> Payments, auth, data migrations, anything that can lose money or data. Let the small stuff through.</p><p><strong>Brain first on risky work.</strong> Write two lines on the problem before you prompt.</p><p><strong>Make PRs explain themselves.</strong> Why this change. What it does. How it does it.</p><p><strong>Ask one “what if” question in review.</strong> What happens on retry? On timeout? On a duplicate?</p><p><strong>Watch the skips.</strong> Count them. Talk about them. Don’t punish them.</p><p><strong>Keep it small.</strong> The moment it feels like another box to click, people will click it without thinking.</p><h3>The bottom line</h3><p>AI can type faster than any of us. Let it.</p><p>Just don’t let it understand on your behalf.</p><p>When the page goes off at 2am, the AI won’t be on call. You will.</p><p>So here’s my question for you. What’s one part of your codebase you’d never ship without fully understanding it?</p><h3>Sources</h3><ul><li>Yuchen Jin on X: <a href="https://x.com/Yuchenj_UW/status/2111865993228406978">https://x.com/Yuchenj_UW/status/2111865993228406978</a></li><li>The Brain Sandwich, Terrible Software: <a href="https://terriblesoftware.org/2026/10/02/the-brain-sandwich/">https://terriblesoftware.org/2026/10/02/the-brain-sandwich/</a></li><li>Hacker News discussion of The Brain Sandwich: <a href="https://news.ycombinator.com/item?id=50139998">https://news.ycombinator.com/item?id=50139998</a></li><li>Show HN: Sphinx: <a href="https://news.ycombinator.com/item?id=50137950">https://news.ycombinator.com/item?id=50137950</a></li><li>Francesco Maggi, “Will it be a golden age or a dark age for mathematics (and science)?”, guest post on Terence Tao’s blog: <a href="https://terrytao.wordpress.com/2026/10/18/will-it-be-a-golden-age-or-a-dark-age-for-mathematics-and-science/">https://terrytao.wordpress.com/2026/10/18/will-it-be-a-golden-age-or-a-dark-age-for-mathematics-and-science/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81d85f93184e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Every layer in a transformer is a memory. Here’s how much each can hold.]]></title>
            <link>https://medium.com/@aditimishra_541/every-layer-in-a-transformer-is-a-memory-heres-how-much-each-can-hold-9cf81d933898?source=rss-f25323722aa0------2</link>
            <guid isPermaLink="false">https://medium.com/p/9cf81d933898</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[transformers]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Aditi Mishra]]></dc:creator>
            <pubDate>Mon, 26 Oct 2026 08:28:08 GMT</pubDate>
            <atom:updated>2026-10-26T08:29:28.375Z</atom:updated>
            <content:encoded><![CDATA[<p>Every layer in a transformer is a memory. Here’s how much each can hold.</p><p>A cache-style way to think about linear layers, MLPs and attention. Plus the numbers from Alex Wang’s viral post, and why some people push back.</p><p>Every engineer knows how a cache works.</p><p>You put in a key. You get back a value.</p><p>Now imagine a cache that doesn&#39;t need the exact key. A key that&#39;s close enough should still get you the right value.</p><p>That&#39;s how Alex Wang asks us to look at a transformer, the design behind today&#39;s large language models. His post, &quot;You could have invented transformers from memory,&quot; got 668 points on Hacker News.</p><p>His big idea: every layer is a memory store. And you can estimate how much each one can hold.</p><h3>The basic idea: a fuzzy key-value store</h3><p>First, a few plain-word definitions.</p><p>A <strong>vector</strong> is a list of numbers. Here, a key is a list of 64 numbers.</p><p><strong>Similarity</strong> is a score for how much two vectors point the same way. Same direction gives a high score. Unrelated gives about zero.</p><p>A <strong>memory</strong> is one key-value pair.</p><p>To recall, you send in a query. The store compares it with the stored keys and returns a value. The research name for this is <strong>associative memory</strong>.</p><p>Here&#39;s the catch. When you recall one memory, the others leak in a little. Wang calls this interference.</p><p>The more you store, the more interference piles up. He tracks one number: the interference-to-signal ratio. When the noise from other memories gets as big as the right answer, the store is full. That point is its capacity.</p><p>His numbers assume random keys and values, each layer on its own, and a query with an exact stored key. Keep that in mind.</p><h3>The linear layer: add everything into one table</h3><p>A <strong>matrix</strong> is a grid of numbers. A <strong>linear layer</strong> is one matrix multiplied by the input.</p><p>The simplest store works like this. For every key-value pair, add a small grid made from that pair into one big matrix. Wang calls this the outer-product store. Every memory is written into the same grid.</p><p>To recall, multiply the query by the matrix. You get the right value, plus a bit of every other value whose key looks similar.</p><p>With 64-number keys, his estimate of capacity is about 64 memories. Basically one memory per number in the key.</p><p>If you solve for the best possible matrix with plain least squares fitting, the limit only doubles, to about 128.</p><h3>A training step writes a memory</h3><p>This is the most surprising part.</p><p><strong>Gradient descent</strong> is how models learn. After each example, every weight gets nudged a little in the direction that cuts the error.</p><p>Wang shows that for a linear layer, one gradient step has exactly the same shape as adding a memory. The key is the layer&#39;s input. The value is a scaled version of the error signal.</p><p>He credits this view to Irie, Csordás and Schmidhuber. They show a linear layer trained this way can be written as attention over all of its training inputs.</p><h3>The MLP: ignore the weak matches</h3><p>An <strong>MLP</strong> (multi-layer perceptron) is two matrices with a simple function in between. It&#39;s the &quot;feed-forward&quot; block inside every transformer layer.</p><p>The problem with the linear store is that every key adds noise, even ones that barely match the query.</p><p>So squash the weak matches before adding things up.</p><p>Wang shows that&#39;s exactly a two-layer MLP. The first matrix holds the keys. The second holds the values. The function in the middle decides how much weak matches count.</p><p>The numbers change a lot depending on that function:</p><ul><li><strong>ReLU</strong> (turns negative scores into zero): about 128.</li><li><strong>Squared ReLU</strong> (also squares the rest, so 0.1 becomes 0.01 while 1 stays 1): about 2,700.</li><li><strong>SwiGLU</strong> (a version of the kind used in today&#39;s language models): about 2,900 in his table.</li><li><strong>Cubed ReLU</strong>: about 35,000.</li></ul><p>So the &quot;about 2,700&quot; figure is for a squared ReLU MLP. A plain ReLU MLP stays near 128.</p><p>This idea isn&#39;t new, and Wang says so. He points to Geva et al., who found that feed-forward layers in trained transformers often act like key-value memories. He also notes that versions 2 and 3 of the original transformer paper called the feed-forward layer &quot;a kind of attention.&quot; That line was later removed.</p><h3>Attention: keep every pair and look it up</h3><p><strong>Self-attention</strong> is the part of a transformer that lets each word look at the other words in the input.</p><p>Higher powers squash weak matches harder. Wang&#39;s trick is to use all the powers at once. That&#39;s the exponential function. Turn its outputs into weights that sum to one, and you get softmax. That&#39;s attention.</p><p>There&#39;s a big difference from an MLP. An MLP&#39;s keys and values are fixed weights learned in training. Attention builds its keys and values from the tokens in your input. Each token stores one memory.</p><p>So the number of memories equals the context length. The store grows as the prompt grows.</p><p>With 64-number keys and the standard sharpness setting, his estimate is about 1.2 million memories. That&#39;s nearly 20,000 times the simple linear store.</p><p>Some long-context models, like Qwen, raise that sharpness as the context grows. In his tests, this kept recall clean past 2 million memories. He describes this as storing &quot;essentially an unbounded number of memories with distinct keys.&quot;</p><h3>Why this helps me think about models</h3><p>Three takeaways I find useful. These are my reading of the post, not claims it makes directly.</p><p><strong>Long context works because attention is a good store.</strong> Its capacity grows with the input. Facts in the prompt are easier to recall cleanly.</p><p><strong>Similar facts get mixed up.</strong> Interference comes from keys that look alike. Two facts with close keys are the ones most likely to blur.</p><p><strong>There&#39;s a reason to keep facts outside the weights.</strong> <strong>RAG</strong> (retrieval-augmented generation) fetches documents and puts them in the prompt. In this picture, it moves facts from the crowded weight store into the attention store. Wang&#39;s reading list even has a section on memory stores that live outside the weights, like kNN-LM and RETRO.</p><h3>The pushback</h3><p>The Hacker News thread had lots of praise, and some real criticism.</p><p><strong>&quot;It&#39;s only an analogy.&quot;</strong> One commenter argued that in real transformers, each layer works on outputs of earlier layers, and training doesn&#39;t update weights the way the post suggests. So the key-value picture is weak.</p><p><strong>&quot;Random keys aren&#39;t real data.&quot;</strong> Another noted that the math assumes random keys and values. Real data has structure, so the numbers are more like bounds than real estimates. Someone replied that the original transformer paper also used random values to explain its scaling.</p><p><strong>&quot;Context isn&#39;t memory.&quot;</strong> One commenter said tokens in the current prompt aren&#39;t memories at all, and the post felt like a story built to make transformers look obvious.</p><h3>What I think</h3><p>The numbers are toy numbers. Wang says so himself.</p><p>But the picture is still useful. A linear layer is a small, noisy table. An MLP is a bigger table that ignores weak matches. Attention is a lookup over everything in front of it.</p><p>Does thinking of layers as memory help you, or does it hide too much?</p><h3>Sources</h3><ul><li><a href="https://alexwang.ai/posts/associative-memory/">Alex Wang, “You could have invented transformers from memory”</a></li><li><a href="https://news.ycombinator.com/item?id=50122878">Hacker News discussion</a></li><li><a href="https://github.com/KeAWang/associative-memory-blog">Code behind the post (KeAWang/associative-memory-blog)</a></li><li><a href="https://arxiv.org/abs/2202.05798">Irie, Csordás and Schmidhuber, “The Dual Form of Neural Networks Revisited” (2022)</a></li><li><a href="https://arxiv.org/abs/2012.14913">Geva et al., “Transformer Feed-Forward Layers Are Key-Value Memories” (2020)</a></li><li><a href="https://arxiv.org/abs/1706.03762v2">Vaswani et al., “Attention Is All You Need,” arXiv version 2</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9cf81d933898" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>