<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://frantic.im/</id>
  <title>frantic.im</title>
  <link rel="self" href="https://frantic.im/feed.xml" />
  <icon>https://frantic.im/favicon.png</icon>
  <subtitle>Occasional posts on technology and stuff</subtitle>
  <updated>2026-04-23T07:37:26.943Z</updated>
  <author>
    <name>Alex Kotliarskyi</name>
  </author>

  
    <entry>
      <id>https://frantic.im/bored</id>
      <title>Willingness to Get Bored</title>
      <updated>2026-03-09T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/bored" />
      <summary></summary>
      <content type="html"><![CDATA[
        
        
        
        <p><a href="https://blog.vjeux.com/">Vjeux</a> once told me a story that stuck with me.</p>
<p>It was about the good old days of the <a href="https://worldofwarcraft.blizzard.com/">World of Warcraft</a>. Blizzard shipped regular updates to the game, but their change logs were very sparse. The community had to figure out the details on their own, and there were a few websites that tried to provide in-depth reviews of the new stuff.</p>
<p>But one of them had an unparalleled level of details. Every little thing was meticulously documented.</p>
<p>People tried to figure out their secret. Did they disassemble the binaries? Did they have someone on the inside leaking the information?</p>
<p>The answer was less magical. The author simply spent hours and days testing the updates manually. It was a lot of labor. Wasn’t it boring? It was. But they were <em>willing to get bored</em> and do the grunt work.</p>
<p>I see parallels to this story everywhere. Progress often comes from someone willing to do boring work. Even writing this blog post required the willingness to get bored editing and rewriting things multiple times.</p>
<p>This is especially insightful today, in the dawn of AI coding. These tools give us so much speed boost and excitement that we are unwilling to slow down.</p>
<p>Everyone I know went through the same experience. The coding agent fails to one-shot the problem in the way its operator expected. The operator either thinks it’s dumb or that they are missing a clever trick to make it work.</p>
<p>But it’s not dumb. And there’s no trick. One just needs to be willing to do the boring work of explaining the context to the model in a <a href="https://openai.com/index/harness-engineering/">durable way</a>.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/suddenly</id>
      <title>Slow, Then Suddenly</title>
      <updated>2026-02-16T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/suddenly" />
      <summary></summary>
      <content type="html"><![CDATA[
        
        
        
        <p>Many years ago, I was invited to an adventure. My colleague from Facebook was assembling a little skunk works crew for a mission at Apple’s headquarters. We were to build the first prototype of the Facebook app for Apple TV. It was 2014.</p>
<p>An engineer from Apple met us at the Infinite Loop. His task was to show us the new APIs and answer technical questions.</p>
<p>We got to work. But instead of building the actual app, we jumped into building the infrastructure. We ported React to work with then not-yet-announced TVML, used the limited APIs of their JS flavor to talk to our GraphQL servers, set up auth, CI and many other developer experience basics.</p>
<p>After a day of this, no progress was made on the actual app. The Apple guy was neither impressed nor disappointed – it was normal for big companies to bring their engineers to Apple’s HQ for “partnership” collaboration sessions that produced zero value.</p>
<p>But we went home excited for the next day. We knew what leverage we built for ourselves. I kept hacking on the CI/CD pipeline well into the night, reducing the overhead down to a few seconds.</p>
<p>The next day started just like the previous one. After coffees and small talk, our host dived into reading the news, expecting nothing significant.</p>
<p>But the next time he looked at the shared monitor connected to the Apple TV, something very strange was happening. We had the first version of the app on the screen. And then the second. And the third. In less than an hour we iterated on several different concepts, the UI changing almost instantly and showing real production data. We had our friends list, photos and videos show up on the TV, we could rearrange everything.</p>
<p>The Apple guy was blown away. From his perspective we made almost no progress at first but then everything happened all at once.</p>
<p>From my perspective, I was having the time of my life. The system translated our intentions into the real product at an insane speed. Any idea you had for the experience, you could make it and see it on the real device right away.</p>
<p>If you look at what’s happening with AI software development early 2026, it does seem very familiar. A handful of engineers investing in their software factories, which from the outside look unnecessarily complicated and are yet to produce anything of value. But don’t dismiss them yet. Pay attention.</p>
<p>Slow at first, but then suddenly all at once.</p>
<p>P.S. That speed was intoxicating, but in the end it didn’t matter. The Facebook Apple TV app never shipped. The leadership poured all resources into mobile and didn’t have time for the side quests.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/remix-3</id>
      <title>Thoughts on Remix 3</title>
      <updated>2025-10-12T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/remix-3" />
      <summary>The pendulum is about to swing the other direction</summary>
      <content type="html"><![CDATA[
        
        
        
        <p><a href="https://remix.run/">Remix</a> is a web framework by <s>React underdogs</s> authors of the most popular React package. A few days ago at <a href="https://remix.run/jam/2025">Remix Jam 2025</a>, Ryan and Michael shared a sneak peek of Remix v3. There’s no official blog post or documentation yet. Here’s my attempt at explaining what it is about.</p>
<p>Remix v1 was a React framework that managed data loading and server-side rendering. Its biggest achievement was the <a href="https://remix.run/">marketing website</a>. Early Remix was interesting because it was viewed as the first real contender for Next.js’s dominant position.</p>
<p>Remix v2 struggled with its messaging and identity. One of the authors <a href="https://x.com/ryanflorence/status/1791479313939976313">described it</a> as <em>“Remix v2 is a Vite plugin that makes access to React Router v6 features more convenient through the Route Module API”</em>. This is definitely not how you beat Vercel’s marketing machine. The framework had its users, but definitely not the trajectory to make a dent in Next.js’s market share.</p>
<p>Remix v3 is… very different. It represents a broader shift in web development sentiment and could be worth paying attention to. To understand why, let’s look at the state of modern React.</p>
<h2>Modern React</h2>
<p><strong>In 2025, React feels complicated.</strong> React Server Components introduced a more sophisticated way to render components on the server and hydrate them on client. The rules of what is allowed are nuanced: server components can be defined as <code>async</code> now and pass data to client components (but not all kinds of data). Regular server-side functions can now be used on client via magic RPC actions. Developers need to learn <a href="https://react.dev/reference/rsc/use-client"><code>&quot;use client&quot;</code></a> vs <a href="https://react.dev/reference/rsc/use-server"><code>&quot;use server&quot;</code></a>.</p>
<p>The speed, especially on big apps, started to be a common concern. Frameworks like Svelte introduced a way to sidestep virtual DOM diffing and produce the most efficient direct way of updating elements, while still looking reasonably declarative. So the React team opened access to the React compiler, a project that has been brewing up inside Meta for almost 8 years. It works by auto-memoizing your components and hooks. But not all components, because that could introduce subtle bugs. Now it’s about <a href="https://react.dev/reference/react-compiler/directives/use-memo"><code>&quot;use memo&quot;</code></a> and <a href="https://react.dev/reference/react-compiler/directives/use-no-memo"><code>&quot;use no memo&quot;</code></a> (aliased as <code>&quot;use forget&quot;</code> and <code>&quot;use no forget&quot;</code> which doesn’t help with the confusion).</p>
<p>The core API surface of React keeps growing. React has added async rendering APIs (<a href="https://react.dev/reference/react/Suspense"><code>Suspense</code></a>, <a href="https://react.dev/reference/react/startTransition"><code>startTransition</code></a>, <a href="https://react.dev/reference/react/useDeferredValue"><code>useDeferredValue</code></a>, <a href="https://react.dev/reference/react/useActionState"><code>useActionState</code></a>) to make web apps more responsive – now user interactions can be prioritized against less urgent but expensive component tree updates, but require you to reason about “active” and “pending” states. The other new APIs are also seen as complex and niche. Just look at the documentation for <a href="https://react.dev/reference/react/useEffectEvent"><code>useEffectEvent</code></a> (but try to guess what it’s doing before you click).</p>
<p><strong>Nobody forces developers to use these features.</strong> In fact even the oldest <code>createReactClass</code> with mixins work just fine with the official <code>create-react-class</code> npm package. You can still use that style of React today, you don’t have to adopt any of the new modern features.</p>
<p>Except, <a href="https://react.dev/learn/creating-a-react-app">you kind of have to</a> – these features are shoved into developers’ throats via <a href="https://nextjs.org/">Next.js</a>. React itself gave up attempts at being a framework. It was just too much work to keep up with fast paced JS tooling, and Facebook had their own setup not worth opensourcing.</p>
<p>Next.js itself went through some painful transitions (<a href="https://nextjs.org/docs/pages">pages router</a> vs <a href="https://nextjs.org/docs/app/getting-started">app router</a>) and left developers who live on the bleeding edge badly hurt. On top of the API churn, Next.js has a whole another layer of complexity caused by Vercel. Vercel deploys fullstack Next.js apps in its own special way – some code runs in the browser, some in AWS Lambda (with 3x markup), and some in its proprietary worker runtime (“middleware” must be one of the biggest lies that caused developer-decades of confusion and frustration).</p>
<p>All this stuff is just… too much. In fact, even explaining the problems these features and frameworks are trying to fix took Dan several <a href="https://overreacted.io/react-for-two-computers/">conferences</a> and <a href="https://overreacted.io/the-two-reacts/">blog posts</a> to describe. People who get it, get it. RSC, Suspense, React Compiler are really powerful and solve real problems some companies have.</p>
<p>On top of that, LLMs suck at React. When asked to build an app, they all reach for React, but the resulting components are ugly soups of <code>useEffects</code> and random hacks to work around subtle bugs.</p>
<h2>Remix v3</h2>
<p>So in this state of complexity and frustration, Remix v3 is born. You can watch the <a href="https://remix.run/jam/2025">Remix Jam</a> recording (look for timestamps in the comments) + scroll through both authors’ X accounts to get a sense of it.</p>
<p>On the frontend Remix still uses JSX, but there’s no React runtime. It doesn’t track state the way React does, but instead gives developers a <code>this.update()</code> function to call to tell Remix that something has changed. Instead of explicit state you can use anything, most commonly closure-captured variables:</p>
<pre><code class="language-typescript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"><span class="hljs-variable language_">this</span>: Remix.Handle</span>) {
  <span class="hljs-comment">// State is just a closure</span>
  <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{count}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;p-2 text-green-500&quot;</span>
        <span class="hljs-attr">on</span>=<span class="hljs-string">{dom.click((event,</span> <span class="hljs-attr">signal</span>) =&gt;</span> {
          count++;
          this.update();
        })}
      &gt;
        Inc
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This makes code more imperative, mechanically simpler. Do this, then do that. Transitions, instead of React’s promise of <code>view = f(state)</code>.</p>
<p>Events are first class and use web’s built-in events mechanism. Instead of <code>onClick</code> there’s a universal <code>&quot;on&quot;</code> prop and a library of standard <code>dom</code> events + developers can define their own <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent"><code>CustomEvents</code></a>.</p>
<p>To control async work, Remix relies on <a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal">signals</a>. E.g. to cancel a <code>fetch</code> when component unmounts the developers can do this:</p>
<pre><code>function Cities(this: Remix.Handle) {
  let list = [], isLoading = true;
  fetch(&quot;https://api.remix.run/cities.json&quot;, {
    signal: this.signal // &lt;-
  })
    .then(response =&gt; response.json())
    .then(data =&gt; {
      list = data;
      isLoading = false;
      this.update();
    });

  return () =&gt; …
}
</code></pre>
<p>Remix will ship with a component library. React has a wide ecosystem of components that won’t work in Remix, so to stay competitive the team is working on high quality built-in components. Things like menus and forms with attention to detail and accessibility support. It also introduces subtle but nice quality-of-life improvements over React: built-in <code>css</code> prop, <code>class</code> instead of <code>className</code>.</p>
<p>On the backend, Remix double-downs on Web Platform. The handlers take web <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request"><code>Request</code></a> and return <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"><code>Response</code></a>. It also brings things like <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData"><code>FormData</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/File"><code>File</code></a> and others to the server. This makes the server runtime an implementation detail. Node, Deno, Bun – all have either native support or adapters for these web APIs, so Remix can run anywhere.</p>
<p>In v3 the framework gave up on file-based routing. Just too much trouble to capture the range of things they want to support in just the file names. Instead it introduces a TypeScript-based way of defining the routes. TypeScript guarantees all routes are implemented and URL parameters are passed to handlers and links are never broken.</p>
<pre><code class="language-typescript"><span class="hljs-keyword">let</span> routes = <span class="hljs-title function_">route</span>({
  <span class="hljs-attr">home</span>: <span class="hljs-string">&quot;/&quot;</span>,
  <span class="hljs-attr">about</span>: <span class="hljs-string">&quot;/about&quot;</span>,
  <span class="hljs-attr">books</span>: {
    <span class="hljs-attr">index</span>: { <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-attr">pattern</span>: <span class="hljs-string">&quot;/&quot;</span> },
    <span class="hljs-attr">create</span>: { <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-attr">pattern</span>: <span class="hljs-string">&quot;/&quot;</span> },
    <span class="hljs-attr">show</span>: <span class="hljs-string">&quot;/books/:slug&quot;</span>,
  },
});

<span class="hljs-comment">// Implementation on server</span>
router.<span class="hljs-title function_">map</span>(routes.<span class="hljs-property">books</span>, booksHandlers)

<span class="hljs-comment">// Reference on clients</span>
&lt;a href={routes.<span class="hljs-property">home</span>.<span class="hljs-title function_">href</span>()}&gt;…&lt;/a&gt;;
</code></pre>
<p>The server also comes with built-in sane things like logging and file storage.</p>
<p><a href="https://www.youtube.com/watch?v=zqhE-CepH2g">The gap</a> is what happens between the client and the server. Remix v3’s approach feels a lot less magical than RSC. There’s no custom JSON hydration streams. Instead Remix reinvents iFrames by creating async loading boundaries and uses HTML as wire format, <a href="https://htmx.org/">HTMX</a>-style. There are also hints of using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components">Web Components</a> to bring together HTML and JS.</p>
<h2>Summary</h2>
<p>Remix keeps leaning into the Web Platform and TypeScript, takes control of a wider portion of the full stack, ditches nuanced parts of React for a simple <code>this.update()</code>, which makes the code less magical and easier to understand for humans and LLMs. Remix is the <a href="https://grugbrain.dev/">Grug Brain</a> version of what Next.js should have been.</p>
<p>So what happens next?</p>
<p>You’ll hear a lot of noise and hot takes (including this post). YouTube influencers are already recording videos and generating surprised faces for video thumbnails to try to take advantage of the next hype wave. Grumpy Hacker News will complain about yet another JS framework.</p>
<p>It will seem like you have to learn Remix now and migrate all your projects to it. You don’t. React is still fine (nobody got fired for choosing React) and you can keep using the good parts (seems to be a <a href="https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742">common JS thing</a>).</p>
<p>Remix v3 is a signal of stronger <a href="https://cassidoo.co/post/annoyed-at-react/">frustrations</a> in React community and desire for a change. The functional ↔ imperative pendulum is starting to swing the other direction.</p>
<p><img src="/figma/og_remix.png" alt="" /></p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/music-player</id>
      <title>A Side Project Story: Music Player</title>
      <updated>2025-10-11T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/music-player" />
      <summary>One of my most successful side projects had exactly 2 daily active users.</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>One of my most successful side projects had exactly 2 daily active users.</p>
<p>When I was growing up I loved listening to music, especially my parents’ cassette tapes and CDs. Back then music had a physical form. You had to take a magical artifact and carefully put it into a special machine that would produce sounds.</p>
<p>My kids growing up experience none of that. From their perspective, we do something with our phones and music just appears from a speaker. The cause and effect far apart.</p>
<p>I wanted to change that. I wanted to give them agency over what’s playing, and create an association between a physical medium, label, picture and song.</p>
<p>So here are the principles I had in mind:</p>
<ul>
<li>One physical object == one song</li>
<li>Adding new songs should be cheap and easy</li>
</ul>
<p>I did a brief research online and couldn’t find anything that fits the requirements. And I’ll be honest – I was delighted, it meant I can actually craft something novel and useful for my family and not just buy off-the-shelf solution.</p>
<p>A few days later, I had the “Music Player” (this was way before I started understanding that a good name is important). Each record was an RFID card with an image and title printed on it. You had to take this simple artifact, tap it at a special device and the music would play.</p>
<p><img src="/figma/music_player_components.png" alt="Components of the project: RFID cards, Raspberry Pi, Spotify and Sonos" /></p>
<p>It was powered by an old Raspberry Pi, simple RFID card reader and my existing Sonos speaker. Spotify served as the music library, and adding new songs there was very easy. Here’s how it worked.</p>
<p>The playback part was pretty <a href="https://github.com/frantic/miniplayer/blob/master/scripts/daemon.js">simple</a>: read RFID tag, find a song, send it to play on Sonos (this was the simplest setup because we already had an old Sonos speaker that we used for playing music).</p>
<p>But what made this project successful was how easy it was to add new songs. When we found a song we liked we’d just add it to a playlist on Spotify.</p>
<p>Which brings me to the secret sauce – the admin panel (not an overkill, I promise). It was a React app that used Spotify API to pull a list of songs from a hardcoded playlist ID. It compared the list with the existing database (stored in Firebase) of RFID ID &lt;&gt; song ID mapping, and if the song was missing from the DB, it would pull artwork, name and artist information from the song metadata. Then it rendered the grid of new songs that had a printer-friendly layout that I printed on a sticker paper, cut into individual labels and stuck them to new RFID cards. Then I used a separate RFID card reader plugged into my computer to quickly associate each card with corresponding song.</p>
<p>This process took me about 10 minutes and my kids would help with it too! After a year we had more than 200 RFID-records.</p>
<p>My kids played music on this Music Player every day for years. The youngest user was less than one year old and barely walked, but took no time to learn that tapping with a card plays the song that’s pictured on the card.</p>
<p>Many friends (with families) that visited our house asked for a system like that. I gave them the instructions but nobody ended up building it. I can’t blame them – this project can feel like too much work to fit into a busy parenting life. If you want to try, the source code (with the shopping list of materials) is here <a href="https://github.com/frantic/miniplayer">https://github.com/frantic/miniplayer</a>.</p>
<p>I loved building it, and my 2 favorite customers loved using it.</p>
<p>P.S. As I was polishing this post I saw a <a href="https://fulghum.io/album-cards">similar project by Jordan Fulghum</a>, he used NFC tags &amp; Plex instead of RFIDs &amp; Spotify. Check it out!</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/get-your-shit-together-day</id>
      <title>The Get Your Shit Together Day</title>
      <updated>2025-09-18T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/get-your-shit-together-day" />
      <summary>Everyone needs a Get Your Shit Together day every once in a while...</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>The Get Your Shit Together day is a day off you take from work, family and other obligations.</p>
<p>It’s a day you dedicate fully to things you’ve been always postponing. Fix squeaky door hinges, repot your plant, publish that blog post, start a side project. Really anything on your todo list that has been there for years.</p>
<p>On the Get Your Shit Together day you are not allowed to work on anything typically considered urgent or important. There’s a whole life-worth of days for that.</p>
<p>The Get Your Shit Together day feels good because you finally take action on things that have been eating away at you for so long. Action creates motivation. Motivation makes you want to do more things.</p>
<p>If you read this far and this idea resonates with you, pick a random day next month and block your calendar. I promise it’ll be worth it.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/who-cares</id>
      <title>Who Cares</title>
      <updated>2025-03-30T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/who-cares" />
      <summary></summary>
      <content type="html"><![CDATA[
        
        
        
        <p>In 2017 I gave a <a href="https://www.youtube.com/watch?v=fjS5ssBn3fA">talk</a> at Chain React about building great user experiences with React Native. At the time the tech was new and the quality of the mobile apps built with React Native was poor. The gist of my talk was that it’s possible to build great apps with React Native, one just needs to care enough.</p>
<p>My message didn’t resonate as well as I’d hoped. It’s hard to make people care about quality when the measuring stick of the technology is “look how fast I made this cross-platform app”. In contrast, inside the early iOS dev community, caring for quality was something that everyone shared.</p>
<p>The modern generation of users grew up without knowing what it feels like to use an offline, low-latency, well-crafted apps and websites. There’s little incentive now to spend any resources on improving quality, while your competition spends it on more features and ads.</p>
<p>I love my AI tools. They cured me from <a href="https://frantic.im/side-projects-are-hard">the blank page curse</a>, they help me learn new tech much faster, and produce boring boilerplate code.</p>
<p>But they also make it easier than ever to not care about quality…</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/hn-stack</id>
      <title>The Hacker News Stack</title>
      <updated>2025-01-28T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/hn-stack" />
      <summary>If you want to build a web app The Hacker News Way, here's the stack you should use.</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>If you want to build a web app The Hacker News Way, here’s the stack you should use:</p>
<ul>
<li><strong>Rust</strong> – the only language that gives you the privilege of calling your app <em>“blazing fast”</em>. According to the undocumented Hacker News rules, any post mentioning Rust in the title starts with 10 points.</li>
<li><strong>Postgres</strong> – the most beloved database that can do absolutely everything: manage data, handle row-level authorization, run job queues, replace Redis, vacuum your apartment (or its own tables… who cares?). No ORMs allowed.</li>
<li><strong>HTMX</strong> – because JavaScript is obviously the worst part about web development. Bonus points: you get to call yourself the <a href="https://htmx.org/essays/lore/#htmx-ceo">CEO of HTMX</a>, very helpful in this turbulent job market.</li>
<li><strong>Hetzner</strong> – for just $4.59/mo you can get a server that can easily handle HN’s famous <a href="https://news.ycombinator.com/item?id=20147951">hug-of-death</a>.</li>
</ul>
<p>Make sure you build your app while WFH, commuting to the office was invented by evil corporations.</p>
<p>…</p>
<p>If this post made your smile, maybe you should cut down on reading Hacker News. I know I should.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/best-tool</id>
      <title>No Best Tool for the Job</title>
      <updated>2024-06-19T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/best-tool" />
      <summary>How do you choose the best tool for the job?</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>How do you choose the best tool for the job?</p>
<p>Suppose you want to plant a tree in your backyard and need to dig a hole. You go to the nearby hardware supply store and see these options:</p>
<p><img src="/figma/graden_tools_options.png" alt="" /></p>
<p>You don’t have to be a PhD in Gardening to know which tool is best for the job. It’s self-evident from the size of the tool and its cost.</p>
<p>The next day you decide to build a web app. You go to a catalog of website-building tools and you see this:</p>
<p><img src="/figma/web_app_tools_options.png" alt="" /></p>
<p>Let’s imagine for a moment that you are relatively new to programming. How would you pick the best tool for the job here?</p>
<h2>All these programming tools look the same</h2>
<p>Unlike tools for digging holes, all these websites look the same. They all promise the same thing — build your web app. It’s not clear why you’d pick one tool over the other. So you dig a little deeper…</p>
<h2>Infinite fractal of categorization</h2>
<p>…and discover an almost infinite amount of specialization. With hardware tools, the categories are more defined: here’s a shovel, and here’s an excavator. There’s nothing in between. With programming languages and frameworks, for any two tools, there seems to be another tool in between.</p>
<p>The job of evaluating the tools becomes harder than the job of actually using the tool.</p>
<p>You could just pick any tool and get started, but…</p>
<h2>Too easy to choose the wrong tool</h2>
<p>With hardware tools, the price and the size of the tool give you a strong hint of what it is best for. Sure, using an excavator to dig a hole for a tree sounds like a ton of fun, but buying or renting one is way too expensive. Plus, you need to learn how to drive one, get a license, and it definitely doesn’t fit into your backyard.</p>
<p>With software tools, it costs almost nothing to choose the wrong tool for the job. They all optimize for getting started quickly. Building a static website with GraphQL — easy. Use Kafka for a one-person backoffice app — no problem. You can spin up both in under 5 minutes, for free.</p>
<p>So how are you supposed to make a good choice for your web app? Well, you could search for some advice on Reddit, X, or HackerNews…</p>
<h2>Advice overload</h2>
<p>…and get overwhelmed with the amount of opposite opinions. “React is the best” / “React is the worst”, you should always render things on the server, you should make a single page app, use static types, use dynamic types, functional programming is the way, functional programming is too slow. The list of contradictions goes on and on.</p>
<p>The people shouting the advice sound extremely confident. If you dig deeper, some of them are actually employed by the tech stack they are preaching, but even outside of obvious economic incentives, it’s just too much noise to sort through.</p>
<p>Meta advice is just as polarized. “Pick a tool you already know” vs “learn new things”, “choose a popular tech stack with a large community” vs “choose niche tech stack with a great community”, “focus on clean code” vs “focus on business outcomes”.</p>
<p>—</p>
<p>So my mind gets <a href="https://frantic.im/side-projects-are-hard/">stuck</a> on this one. How do you pick the best tool for the job?</p>
<p>The only way out of this pickle situation I found so far is to turn around and face the question itself. What does “the best” even mean?</p>
<p>Back to the tree planting, the choice is simple, so it’s obvious which tool is the best. In programming, we no longer have <a href="https://youtu.be/0Ttw9ks05G4?si=XR4z-Jwj825p7fkI&amp;t=222">that luxury</a>, thus maybe the concept of “the best” doesn’t work anymore. The options are just too close, too nuanced, and the consequences are too fuzzy. Even if you are an expert in these tools, there are still enough unknowns to make the choice non-trivial.</p>
<p>When the options can’t be plotted on a single axis with clear “good” and “bad” fits, there’s no “best”. There’s only “good enough”. And if we give up chasing “the best tool for the job”, we might just free up enough mental space to use “the good enough tool” to actually plant that tree.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/opening-mail</id>
      <title>Opening Mail</title>
      <updated>2024-01-06T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/opening-mail" />
      <summary>First make the change easy, then make the easy change.</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>I never liked opening envelopes; they’re tricky and ripping them open is annoying. My letters would get stuck or tear with the envelope.</p>
<p>The mail just used to stack up, and I’d miss important stuff because of it.</p>
<p>But then I found this cool little gadget from Japan.</p>
<a style="border: none" href="https://www.amazon.com/dp/B001GR4DQ8" target="_blank">
  <img src="https://frantic.im/assets/ceramic-letter-opener.jpg" width="256">
</a>
<p>It’s well-made, affordable, and feels good to use. Plus, it’s safe.</p>
<p>The best part? It actually made me enjoy opening my mail.</p>
<p>After this experience, I started thinking differently about unpleasant tasks. Is there a tool or a service that add delight to mundane things?</p>
<p>I also started noticing when people do this subconsciously. For example, most software engineers I know hate blogging, but they like building their own blog engine to make blogging more pleasant (I’m very guilty of this too).</p>
<p>Kent Beck <a href="https://twitter.com/KentBeck/status/250733358307500032">nailed it</a>: “for each desired change, make the change easy (warning: this may be hard), then make the easy change”.</p>

      ]]></content>
    </entry>
  
    <entry>
      <id>https://frantic.im/whos-watching-the-watchdog</id>
      <title>Who&#39;s Watching the Watchdog?</title>
      <updated>2023-11-22T12:00:00+00:00</updated>

      <link rel="alternate" href="https://frantic.im/whos-watching-the-watchdog" />
      <summary>Making reliable systems that expect things to go wrong</summary>
      <content type="html"><![CDATA[
        
        
        
        <p>At my current company we have an automated pipeline for processing customers’ orders. It’s pretty complex — talking to multiple different services, training models, storing large files, updating the database, sending emails and push notifications.</p>
<p>Sometimes things get stuck because of a temporary 3rd party outage or a bug in our code.</p>
<p>So we built a watchdog service: it monitors the stream of orders and makes sure the orders get processed within reasonable timeframe (3 hours). The watchdog only looks at the final invariant — was the order fulfilled and delivered to the customer? It doesn’t care about any intermediary steps.</p>
<p>This system has saved us many times. When the watchdog finds a stuck order, it posts in our special channel in Slack. We investigate the problem and address the root cause, so hopefully we won’t see new orders stuck for the same reason.</p>
<p>But who’s watching the watchdog? What if it fails to run?</p>
<p>It actually happened to us once. The watchdog is running on the job scheduling system, and that system went down. That meant no orders were getting processed and watchdog also wasn’t running. The alerts channel in Slack was blissfully silent.</p>
<p>To address this case, we need a system that can watch the watchdog. We are using these two:</p>
<ul>
<li><a href="https://docs.sentry.io/product/crons/">Sentry Cron</a></li>
<li><a href="https://www.checklyhq.com/blog/heartbeat-monitoring-with-checkly/">Checkly Heartbeat</a></li>
</ul>
<p>The idea behind both systems is the same: they expect a regular cron job to “check in” on a pre-defined schedule. If it misses a check-in, there’s likely a problem and we get an alert in Slack.</p>
<p>Complex systems always find surprising ways to fail. When adding an end-to-end quality watchdog (and ways to watch the watchdog) you can create a positive loop of detecting issues and hardening the system.</p>

      ]]></content>
    </entry>
  
</feed>