<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog on Avi Chandra</title><link>https://avichandra.com/blog/</link><description>Recent content in Blog on Avi Chandra</description><generator>Hugo</generator><language>en-us</language><copyright>© Avi Chandra</copyright><lastBuildDate>Mon, 31 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://avichandra.com/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Editing the same file on two computers</title><link>https://avichandra.com/blog/same-file-two-computers/</link><pubDate>Mon, 31 Aug 2026 00:00:00 +0000</pubDate><guid>https://avichandra.com/blog/same-file-two-computers/</guid><description>&lt;p&gt;Here&amp;rsquo;s a problem that sounds simple until you actually sit down and try to solve it.&lt;/p&gt;&#10;&lt;p&gt;You&amp;rsquo;re building a local-first app. Let&amp;rsquo;s call it &lt;strong&gt;Thangs&lt;/strong&gt;, a Things 3 clone for people who like their to-do lists fast, offline, and free of subscription fatigue. The whole pitch is that your data lives in a file on your machine, and if you want sync, you just drop that file in Dropbox or iCloud and let the cloud do its dumb cloud thing.&lt;/p&gt;</description></item><item><title>Arrhenotoky and AI swarms</title><link>https://avichandra.com/blog/arrhenotoky-and-ai-swarms/</link><pubDate>Mon, 24 Aug 2026 00:00:00 +0000</pubDate><guid>https://avichandra.com/blog/arrhenotoky-and-ai-swarms/</guid><description>&lt;p&gt;There&amp;rsquo;s a word I want you to sit with for a second: &lt;strong&gt;arrhenotoky&lt;/strong&gt;. It sounds like a neighborhood in Athens where you&amp;rsquo;d get a very good lamb dish. It is actually one of the strangest reproductive systems on Earth, and I&amp;rsquo;ve become convinced it says something about where AI systems might be headed. Not human-shaped. Hive-shaped.&lt;/p&gt;&#10;&lt;p&gt;Bear with me. The biology comes first, and the biology is the good part.&lt;/p&gt;</description></item><item><title>A sync engine is mostly conflict policy</title><link>https://avichandra.com/blog/conflict-policy/</link><pubDate>Thu, 30 Jul 2026 00:00:00 +0000</pubDate><guid>https://avichandra.com/blog/conflict-policy/</guid><description>&lt;p&gt;Every time I start a sync engine I spend the first week on transport, and every&#10;time that turns out to be the week that did not matter.&lt;/p&gt;&#10;&lt;h2 id="transport-is-solved"&gt;Transport is solved&lt;/h2&gt;&#10;&lt;p&gt;Batching, retries, resumable cursors, a version vector so you know where you&#10;left off:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Cursor&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;replica&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;clock&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;number&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pull&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;since&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;Cursor&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;Op&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;[]&lt;/span&gt;&amp;gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// page until the server says it has nothing newer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are a dozen good write-ups on all of it, and the code ends up looking the&#10;same regardless of who wrote it.&lt;/p&gt;</description></item><item><title>Shipping to one campus at a time</title><link>https://avichandra.com/blog/one-campus-at-a-time/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><guid>https://avichandra.com/blog/one-campus-at-a-time/</guid><description>&lt;p&gt;mrmr launched at one university before it launched at two. That was not a&#10;growth strategy, it was the only thing that worked.&lt;/p&gt;&#10;&lt;h2 id="the-same-software-three-different-products"&gt;The same software, three different products&lt;/h2&gt;&#10;&lt;p&gt;An anonymous feed changes character entirely with size, and nothing in the code&#10;changes with it:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;~100 people.&lt;/strong&gt; It is a group chat.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;~1,000 people.&lt;/strong&gt; It is a newspaper.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;10,000 strangers.&lt;/strong&gt; It is a sewer.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Only the boundary moved.&lt;/p&gt;&#10;&lt;h2 id="scope-does-the-work-a-filter-cannot"&gt;Scope does the work a filter cannot&lt;/h2&gt;&#10;&lt;p&gt;Restricting the feed to a single campus handles moderation in a way no&#10;classifier has managed for me. Everyone posting is someone you could plausibly&#10;stand next to in a queue tomorrow, and that fact alone removes most of what&#10;makes anonymity grim.&lt;/p&gt;</description></item><item><title>Frame buffers beat photos</title><link>https://avichandra.com/blog/frame-buffers/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://avichandra.com/blog/frame-buffers/</guid><description>&lt;p&gt;The first version of the Lorcana scanner had a shutter button. You lined up a&#10;card, tapped, waited, got a result. About three seconds a card, and your thumb&#10;hurt after a playset.&lt;/p&gt;&#10;&lt;p&gt;The second version never captures a photo at all.&lt;/p&gt;&#10;&lt;h2 id="read-the-buffer-skip-the-capture"&gt;Read the buffer, skip the capture&lt;/h2&gt;&#10;&lt;p&gt;Recognition runs on whatever is already flowing through the capture pipeline:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;captureOutput&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;_&lt;/span&gt; output: AVCaptureOutput,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; didOutput sampleBuffer: CMSampleBuffer,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from connection: AVCaptureConnection) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; pixels = CMSampleBufferGetImageBuffer(sampleBuffer) &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recognizer.submit(pixels) &lt;span style="color:#75715e"&gt;// no still capture, no pipeline flush&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The whole speed win comes from deleting the capture step:&lt;/p&gt;</description></item></channel></rss>