<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Manu Martínez-Almeida — Writing</title><description>Essays, project write-ups, and experiments — compilers, graphics, infrastructure, and open source.</description><link>https://manualmeida.dev/</link><atom:link href="https://manualmeida.dev/rss.xml" rel="self" type="application/rss+xml"/><item><title>Qwik: Resumability That Feels Like React</title><link>https://manualmeida.dev/articles/qwik-resumability/</link><guid isPermaLink="true">https://manualmeida.dev/articles/qwik-resumability/</guid><description>I joined Miško Hevery&apos;s team and helped turn Qwik from a resumable-but-painful prototype into a framework that feels like React, backed by a Rust compiler that extracts closures and serializes app state into HTML, so the fast path is the default one.</description><pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Performance is a human problem, not a technology one&lt;/h2&gt;
&lt;p&gt;The thing I came to believe working on Qwik is that the web’s performance problem isn’t a
technology problem. It’s a design and developer-experience problem. Developers, like water, follow the
path of least resistance, and in most frameworks the easy way to build something is also the way that
ships a slow site. We then tell people to go optimize it afterward, as a chore, trading off
capabilities or DX or their weekend to claw the performance back.&lt;/p&gt;
&lt;p&gt;I find it more honest to stop blaming developers and fix the path instead. A framework should be
designed so the path of least resistance lands you on a fast site by default. You shouldn’t have to be
a performance expert to ship a fast page; you should have to work to ship a slow one.&lt;/p&gt;
&lt;p&gt;And the thing worth optimizing is almost always JavaScript. Most sites already handle their images and
CSS reasonably well, so there’s little left on the table there. JavaScript is where the big wins hide.
The difference between a sluggish page and a snappy one is often tens of points of Lighthouse score, and
for anyone running an e-commerce or consumer site, that gap is revenue. Resumability is the lever Qwik
pulls on exactly that.&lt;/p&gt;
&lt;h2&gt;The idea: resume, don’t rebuild&lt;/h2&gt;
&lt;p&gt;Every mainstream framework hydrates. The server renders HTML, then the browser downloads the whole
component tree as JavaScript, re-executes it, and reattaches event listeners to reconstruct state the
server already had. You pay, in bytes and CPU, to rebuild something you were handed seconds ago.&lt;/p&gt;
&lt;p&gt;Islands and partial hydration, the approach Astro popularized, make this better by only hydrating
the interactive bits. But each island still has to download and execute its JavaScript before it’s
ready, and someone has to draw and maintain those island boundaries by hand.&lt;/p&gt;
&lt;p&gt;Resumability skips the rebuild entirely. The server serializes the application’s state and its event
wiring into the HTML itself. The browser ships almost no JavaScript up front. Conceptually the app
becomes a hashmap: an event on an element points at the one chunk of code that handles it. When you
click a button, Qwik reads an attribute on that element, fetches that small chunk, restores the state
it captured, and runs it. There’s no global “boot the app” step, because there’s no app to boot.&lt;/p&gt;
&lt;p&gt;There’s a neat way to see the flip. In a hydrated app, event handlers are the &lt;em&gt;last&lt;/em&gt; thing to become
ready, after the whole tree has downloaded and executed. In a resumable app they’re the &lt;em&gt;first&lt;/em&gt;. The
page is interactive the moment it arrives.&lt;/p&gt;
&lt;h2&gt;The first version worked; writing it was the hard part&lt;/h2&gt;
&lt;p&gt;Proving resumability was one thing. The developer experience was another. In &lt;a href=&quot;https://github.com/QwikDev/qwik/tree/a64a648e6b12ec5acd1e55decd734cb3b474b7af/integration/todo/ui/Header&quot;&gt;that early version&lt;/a&gt;
the philosophy leaked all the way into the API. You split your code into many small files by hand, you
referenced lazy-loadable symbols by name, and you wrote a lot of ceremony to tell the runtime how the
pieces fit together. It worked, and it asked too much of the person typing.&lt;/p&gt;
&lt;p&gt;My bet was that all of that ceremony could become the compiler’s job. A developer should write
something that looks like ordinary &lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;, with components, event handlers, and local state, and the
build step should do the hard work of making it resumable. The point of the whole project, after all,
was to make the fast path the easy path, and an API that demanded this much manual wiring was the
opposite of that.&lt;/p&gt;
&lt;h2&gt;Making it feel like React&lt;/h2&gt;
&lt;p&gt;In the design I led, you write a component with handlers and hooks, the way you already think. The
magic lives in the compiler.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const Counter = component$(() =&amp;gt; {
  const count = useSignal(0);
  // looks like an ordinary closure; the compiler will extract it
  return &amp;lt;button onClick$={() =&amp;gt; count.value++}&amp;gt;{count.value}&amp;lt;/button&amp;gt;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler reads the AST, finds every &lt;code&gt;$&lt;/code&gt;-marked boundary (a &lt;code&gt;component$&lt;/code&gt;, an &lt;code&gt;onClick$&lt;/code&gt;), and
pulls that closure out into its own module with no dependencies except the variables it closed over.
Those captured variables get serialized straight into the HTML. When the user clicks, the runtime
loads that one module, restores the state it captured, and runs the handler. Nothing else downloads.&lt;/p&gt;
&lt;p&gt;The captured state can be a live signal, so the handler reads and updates a reactive &lt;code&gt;count&lt;/code&gt; and the
DOM follows along. &lt;code&gt;useSignal&lt;/code&gt; lets the runtime make a surgical update to exactly the text node that
changed, with no component re-render in the browser. The signal model drew on Solid’s
&lt;a href=&quot;https://www.solidjs.com/&quot;&gt;reactivity&lt;/a&gt;. The result reads like React and behaves like a resumable app,
with none of the manual wiring the first version demanded.&lt;/p&gt;
&lt;p&gt;One thing I like about this model over React Server Components: a Qwik component is universal. The same
component runs on the server or the client, and the developer doesn’t annotate the boundary. With RSC
it’s easy to reach for &lt;code&gt;use client&lt;/code&gt; everywhere out of convenience and quietly opt back into shipping
everything. In Qwik there’s no boundary to get wrong, because the compiler is the one deciding what
crosses it.&lt;/p&gt;
&lt;h2&gt;Rust and SWC&lt;/h2&gt;
&lt;p&gt;The optimizer that does all this is written in Rust, on top of &lt;a href=&quot;https://swc.rs/&quot;&gt;SWC&lt;/a&gt;. It runs on
every build and across large codebases, so it had to stay fast enough to disappear into the dev loop.
Rust kept the AST passes quick as projects grew, which matters when the entire premise is that the
compiler, and not the developer, carries the complexity.&lt;/p&gt;
&lt;h2&gt;QwikCity&lt;/h2&gt;
&lt;p&gt;Later, with Adam Bradley (co-creator of Ionic and Stencil), I worked on the design of QwikCity, the
meta-framework that sits on top: routing, data loading, and the conventions that make Qwik a way to
build whole sites. That part was a close back-and-forth between the two of us.&lt;/p&gt;
&lt;h2&gt;Credit where it’s due&lt;/h2&gt;
&lt;p&gt;I want to be precise about this. The resumability concept, and much of how it works under the hood,
including the runtime and the Solid-inspired signals, came from Miško Hevery, the creator of
&lt;a href=&quot;https://angular.dev/&quot;&gt;Angular&lt;/a&gt;.
What I led was the developer-facing redesign and the compiler beneath it: turning closures into
independent, serializable modules and working out the reachability analysis that decides what to ship.&lt;/p&gt;
&lt;p&gt;The throughline to the rest of my work is that compiler. Make the source look ordinary, push the hard
analysis to build time, and let the runtime stay lazy, so the path of least resistance is also the
fast one. I’d wired the same instinct into
&lt;a href=&quot;https://manualmeida.dev/articles/gin-simple-over-easy&quot;&gt;Gin’s router&lt;/a&gt; years earlier, and I’d reach for it again tomorrow.
If you’re designing a framework, that’s where I’d spend the effort: on the compiler that lets people
write the easy thing while you quietly ship the simple one.&lt;/p&gt;
&lt;p&gt;If you want the longer version of this argument, I gave a
&lt;a href=&quot;https://www.youtube.com/watch?v=HfEDhuZKH7A&quot;&gt;talk on it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://manualmeida.dev/articles/qwik-resumability&quot;&gt;This article has interactive figures — read it on manualmeida.dev.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Open Source</category><category>qwik</category><category>compilers</category><category>rust</category><category>performance</category><category>web</category></item><item><title>I Broke My Ankle, Then Rendered It in a Browser</title><link>https://manualmeida.dev/articles/sethealth-gpu-ct/</link><guid isPermaLink="true">https://manualmeida.dev/articles/sethealth-gpu-ct/</guid><description>In October 2019 I broke my ankle in Berlin, ended up with my own CT scan on a CD, and started rendering gigabytes of it in a browser. That became Sethealth, a GPU-accelerated medical-imaging platform.</description><pubDate>Wed, 20 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In October 2019 I went out for drinks with friends in Kreuzberg, a few minutes from my office in
Berlin. I broke my ankle that night. Everything I’d been building stopped at once, and for the first
time in years I had nothing to ship.&lt;/p&gt;
&lt;h2&gt;Fourteen days to think&lt;/h2&gt;
&lt;p&gt;I spent fourteen days in a Kreuzberg hospital, waiting for the swelling to go down before they could
operate. Once I calmed down, it wasn’t all bad. I read the books I’d been meaning to get to, reached
out to people I’d lost touch with, designed a tattoo for the ankle, and learned enough German to order
a coffee with milk and no sugar.&lt;/p&gt;
&lt;p&gt;Mostly, it switched off the autopilot. I’d gone years without a single week away from code, and the
quiet was the first chance I’d had to think about what I wanted to build next.&lt;/p&gt;
&lt;h2&gt;My data was mine, and I couldn’t reach it&lt;/h2&gt;
&lt;p&gt;The surgery worried me even after the doctors said it had gone well. I wanted a second opinion, and
that meant getting my own scans out of the hospital. It turned into a small ordeal. I left with my
medical imaging burned onto a CD, then ordered a USB CD reader from Amazon so I could open my own body
on my own laptop.&lt;/p&gt;
&lt;p&gt;The second opinion sent me straight back to Spain, where my family is. There were real problems: the
joint spacing was off, there was nerve damage from the suture, an unrepaired tendon, and a screw
sitting in the wrong place. A second surgery fixed it, and I finally found some calm.&lt;/p&gt;
&lt;p&gt;Somewhere in that mess the product idea formed. Patients own their medical data. Reaching it, reading
it, and sharing it is absurdly hard.&lt;/p&gt;
&lt;h2&gt;Visualizing my own ankle&lt;/h2&gt;
&lt;p&gt;With no deadlines left, I opened my laptop and went back to something I hadn’t touched in years:
writing rendering code from scratch, the way I used to build &lt;a href=&quot;https://manualmeida.dev/articles/about&quot;&gt;game engines&lt;/a&gt;. I wanted
to see my own scan in 3D, in a browser, with nothing to install. The first time my ankle rotated on
screen, rendered from the CD I’d fought to get, I knew this was the thing I wanted to work on.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/ray-tracing.jpg&quot; alt=&quot;A ray-traced 3D reconstruction of a fractured ankle, the bones reassembled from a CT scan&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;span&gt;Figure 1.&lt;/span&gt; My own ankle, reconstructed from the CT scan on that CD and ray-cast in the browser. The loose fragments along the right are the displaced pieces of the break.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Rendering gigabytes of voxels in a browser&lt;/h2&gt;
&lt;p&gt;A CT scan is a 3D grid of density samples, often hundreds of slices deep, which adds up to gigabytes.
You can’t download all of it before drawing anything, and you can’t fit all of it in GPU memory. Yet
a radiologist expects to spin and re-window the volume with no lag.&lt;/p&gt;
&lt;p&gt;The rendering itself is ray casting through a 3D texture. For each pixel on screen, march a ray into
the volume, sample density along the way, map each sample to color and opacity, and composite front to
back until the pixel goes opaque.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// simplified: march a ray through the CT volume and accumulate color + opacity
vec4 raymarch(vec3 origin, vec3 dir) {
  vec4 acc = vec4(0.0);
  for (int i = 0; i &amp;lt; STEPS; i++) {
    vec3 p = origin + dir * (float(i) * stepSize);
    float density = texture(volume, p).r;    // sample one voxel
    vec4 s = transfer(density);              // density -&amp;gt; color + alpha
    acc.rgb += (1.0 - acc.a) * s.a * s.rgb;  // front-to-back compositing
    acc.a   += (1.0 - acc.a) * s.a;
    if (acc.a &amp;gt; 0.99) break;                 // early ray termination
  }
  return acc;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three decisions made it run in real time on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt;. We split each volume into smaller blocks, so the
GPU only ever held the parts in view and could skip the empty space between them. We quantized the
data, including the normal vectors used for shading, to shrink what had to live in texture memory. And
we loaded progressively, coarse first and detail as you leaned in, so the first frame appeared fast
even on a multi-gigabyte study.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/openview.png&quot; alt=&quot;OpenView Health, a browser viewer showing a brain MRI in four synchronized panes: axial, coronal, sagittal slices and a 3D angiography render, with measurements and metadata&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;span&gt;Figure 2.&lt;/span&gt; OpenView Health, the free viewer we built on the Sethealth SDK. Slices and the 3D render stay in sync, and you can measure, window, and annotate straight in the browser.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Keeping the data safe by never seeing it&lt;/h2&gt;
&lt;p&gt;Medical data raises the stakes on security, so we ran end-to-end encryption on the client. A scan
never left the device in the clear, which meant we couldn’t read it, and that was the point. The data
we never held unencrypted is data we could never leak. That one decision collapsed a large amount of
compliance and liability surface into something we could reason about.&lt;/p&gt;
&lt;h2&gt;What it became&lt;/h2&gt;
&lt;p&gt;Sethealth shipped as an SDK and a compliant backend across iOS, Android, and the web, and it ran until
2026. The use case that clicked was custom prosthetics. A doctor could shape an implant against a
patient’s real anatomy, an engineer could validate it, and the two could hold a precise conversation
inside the same 3D view, all running on our SDK.&lt;/p&gt;
&lt;figure&gt;
  &lt;div&gt;
    &lt;img src=&quot;https://manualmeida.dev/articles/sethealth/custom-implants.png&quot; alt=&quot;A custom-implant ordering tool built on the Sethealth SDK, showing a fractured ankle with surgical plates and screws planned over the CT scan, alongside controls to order implants, surgical guides, and 3D-printed bio-models&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;span&gt;Figure 3.&lt;/span&gt; A custom-implant planning and ordering tool a partner built on the SDK: plan plates, screws, and surgical guides against a real fracture, leave notes, and order the printed parts. The tool is no longer online.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I built it on a simple conviction. Healthcare carries more weight than most software, and patients are
the rightful owners of their data even when the system makes that ownership painful to exercise.
Sethealth was my bottom-up attempt to hand some of that ownership back.&lt;/p&gt;
&lt;p&gt;The deeper lesson came from the ankle. For years I never went a week without coding, and I’d mistaken
motion for progress. Stepping back to ask why, and not only how, paid off more than any sprint I can
remember. You don’t need to break a bone to get there. Close the laptop for a week and notice which
idea refuses to leave you alone.&lt;/p&gt;
</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Graphics</category><category>gpu</category><category>webgl</category><category>graphics</category><category>medical imaging</category><category>performance</category></item><item><title>Building Gin: Simple Over Easy</title><link>https://manualmeida.dev/articles/gin-simple-over-easy/</link><guid isPermaLink="true">https://manualmeida.dev/articles/gin-simple-over-easy/</guid><description>A stalled startup in 2014 became Gin: a Go web framework shaped by simple-over-easy design, a radix-tree router, and an API that still works ten years later.</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2014 I came back from San Francisco with no plan and one useful scar: I had seen what small
software teams needed from their tools. I had spent a year building SDKs at Joypad and TinySpark after
shipping one of my first games. Then I was back in Spain, about to start Telecommunications
Engineering, and trying to decide what to build next.&lt;/p&gt;
&lt;p&gt;The answer was Fyve, a social network built around people’s interests. I chose &lt;a href=&quot;https://go.dev/&quot;&gt;Go&lt;/a&gt;
for the backend because the language felt plain in the right way. &lt;a href=&quot;https://gin-gonic.com/&quot;&gt;Gin&lt;/a&gt; started
as the web framework for that product, written while I was still learning Go and still deciding what kind
of engineer I wanted to be. The code lives at &lt;a href=&quot;https://github.com/gin-gonic/gin&quot;&gt;gin-gonic/gin&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;https://manualmeida.dev/articles/gin/golang-framework-gin-gonic.png&quot; alt=&quot;Gin Gonic Go framework illustration&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Fyve faded. Gin kept going.&lt;/p&gt;
&lt;h2&gt;Simple over easy&lt;/h2&gt;
&lt;p&gt;At the time, the Go web framework people kept pointing me to was
&lt;a href=&quot;https://github.com/go-martini/martini&quot;&gt;Martini&lt;/a&gt;. I understood why immediately. The README was small,
the middleware model felt elegant, and you could get a route responding in minutes.&lt;/p&gt;
&lt;p&gt;Martini used reflection-based dependency injection to wire handlers together. That made the first demo
feel smooth. It also moved important behavior out of sight. Services appeared in handlers by magic,
control flow became harder to trace, and reflection ran on the request path.&lt;/p&gt;
&lt;p&gt;I was reading the Go community through Rob Pike’s
&lt;a href=&quot;https://www.youtube.com/watch?v=rFejpH_tAHM&quot;&gt;Simplicity is Complicated&lt;/a&gt; lens around then. The line that
stuck with me was not a slogan about minimalism. It was the cost model: simple software often takes
more work from the person building it so it can take less work from the person using it.&lt;/p&gt;
&lt;p&gt;That became the design line for Gin. &lt;strong&gt;Easy is what looks good in the first example:&lt;/strong&gt; fewer lines,
nicer syntax, less ceremony on the page. &lt;strong&gt;Simple is lower count:&lt;/strong&gt; fewer moving parts, fewer concepts to
learn, fewer exceptions to remember. Martini made the first version easy. I wanted Gin to stay simple
after the codebase was old enough to surprise me.&lt;/p&gt;
&lt;h2&gt;Finding the middle ground&lt;/h2&gt;
&lt;p&gt;Aristotle’s version of virtue was the middle ground: &lt;strong&gt;not too much, not too little.&lt;/strong&gt; That was the shape
of the framework problem too.&lt;/p&gt;
&lt;p&gt;Martini gave you too much magic. Go’s standard &lt;a href=&quot;https://pkg.go.dev/net/http&quot;&gt;net/http&lt;/a&gt; gives you the
honest version of web programming: no magic, explicit control flow, and a handler shape you can hold in
your head. But it gives you too little help. You write the same plumbing for route params, request
parsing, validation, and responses in handler after handler. None of it is hard. Enough of it becomes
noise.&lt;/p&gt;
&lt;p&gt;Gin was my attempt to find the useful point between them: keep the request path explicit, run no
reflection there, and put the boring work behind one object you can inspect: the Context.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;r := gin.Default()
r.GET(&quot;/users/:id&quot;, func(c *gin.Context) {
    id := c.Param(&quot;id&quot;)          // path params, no reflection
    c.JSON(200, gin.H{&quot;id&quot;: id}) // response rendering, one call
})
r.Run(&quot;:8080&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;*gin.Context&lt;/code&gt; carries the request, response writer, path parameters, validation helpers, and
rendering. You pass one thing around. The obvious operations stay one method call away, while the
machinery remains visible enough that you can debug it when production gets weird.&lt;/p&gt;
&lt;p&gt;Funny enough, &lt;code&gt;gin.Context&lt;/code&gt; shipped in 2014, two years before Go’s standard library had a
&lt;a href=&quot;https://pkg.go.dev/context&quot;&gt;&lt;code&gt;context.Context&lt;/code&gt;&lt;/a&gt;. When it landed, we didn’t rename ours — we made
&lt;code&gt;gin.Context&lt;/code&gt; satisfy the standard interface, adding full compatibility without a single breaking change.&lt;/p&gt;
&lt;p&gt;That was the product sense from SDK work showing up in a web framework. Developer experience is a
feature when it makes the fast path feel natural.&lt;/p&gt;
&lt;h2&gt;A router built around a radix tree&lt;/h2&gt;
&lt;p&gt;The router is where the simplicity line became concrete. Martini could walk a list of regular
expressions and ask each one whether it matched. That is flexible. You can make a route match only
numbers, or hide extra rules inside the pattern. It is also another language inside your framework.&lt;/p&gt;
&lt;p&gt;Gin chose a smaller route language: static segments, named parameters, and catch-alls. That choice made
the router faster because it could use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Radix_tree&quot;&gt;radix tree&lt;/a&gt;, the same
approach &lt;a href=&quot;https://github.com/julienschmidt/httprouter&quot;&gt;httprouter&lt;/a&gt; made popular in Go. More importantly, it made routes easier to reason about. The framework pushed you
toward regular paths instead of clever matchers.&lt;/p&gt;
&lt;p&gt;Matching &lt;code&gt;/blog/42/comments&lt;/code&gt; walks &lt;code&gt;/blog/&lt;/code&gt; down to the &lt;code&gt;:slug&lt;/code&gt; node, binds &lt;code&gt;42&lt;/code&gt;, and continues into
&lt;code&gt;/comments&lt;/code&gt;. The work follows the length of the URL, not the number of routes registered in the app.
Routes that look separate in a file collapse into one compact path through memory.&lt;/p&gt;
&lt;p&gt;For a router holding $n$ routes and a request path of length $k$, a radix-tree lookup runs in&lt;/p&gt;
&lt;p&gt;$$
T_\text{match}(k) = O(k), \quad \text{independent of } n,
$$&lt;/p&gt;
&lt;p&gt;where linear regex matching costs $O(n \cdot m)$ for $m$-length patterns. The tree trades that
per-request scan for a single walk down the shared prefix.&lt;/p&gt;
&lt;p&gt;The same idea showed up in the small allocation choices. Parameters live in a preallocated slice. Context
objects come from a &lt;code&gt;sync.Pool&lt;/code&gt; and get reset between requests. The garbage collector gets less junk to
clean up, so latency has fewer reasons to wobble.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That is the kind of performance work I trust: fewer operations on the hot path, and fewer concepts in
the programmer’s head.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Designing for zero breaking changes&lt;/h2&gt;
&lt;p&gt;The quiet goal was zero breaking changes. I wanted Gin to feel like it belonged in Go’s ecosystem, which
meant learning from Go’s own &lt;a href=&quot;https://go.dev/doc/go1compat&quot;&gt;compatibility promise&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That constraint changes how you design. You add a method before you remove one. You reject the clever
rename that saves five characters. You treat every public function as something a stranger might build
a company on.&lt;/p&gt;
&lt;p&gt;It held. Some of the first programs written against Gin still compile and run more than ten years
later. &lt;strong&gt;The benchmarks matter. That compatibility matters more.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Hacker News, and then growth&lt;/h2&gt;
&lt;p&gt;I &lt;a href=&quot;https://news.ycombinator.com/item?id=7966700&quot;&gt;released Gin on Hacker News&lt;/a&gt; at the right moment. Go was
getting attention there, and a framework that fit in one README, avoided reflection on the hot path, and
benchmarked well was easy to try.&lt;/p&gt;
&lt;p&gt;The growth after that was steady rather than cinematic. People used it, filed issues, sent patches,
and put it in real services. Today Gin sits around 88k stars with more than 290k projects depending on
it.&lt;/p&gt;
&lt;p&gt;Open source numbers can be vanity metrics. In this case, &lt;strong&gt;the dependency count matters more to me than
the stars.&lt;/strong&gt; It means the API decisions kept compounding long after the original startup disappeared.&lt;/p&gt;
&lt;h2&gt;Letting it graduate&lt;/h2&gt;
&lt;p&gt;A few years in, I stepped back and handed Gin to maintainers who kept improving it without me. I think
of that as the project graduating. Special kudos to &lt;a href=&quot;https://github.com/appleboy&quot;&gt;Bo-Yi Wu&lt;/a&gt; and
&lt;a href=&quot;https://github.com/javierprovecho&quot;&gt;Javier Provecho&lt;/a&gt;, who carried it forward and kept the bar high.&lt;/p&gt;
&lt;p&gt;The part of open source I respect is when &lt;strong&gt;the thing stops needing its author.&lt;/strong&gt; It survives contact
with other people’s use cases, other people’s priorities, and other people’s taste.&lt;/p&gt;
&lt;p&gt;If you’re building a library, that is the bar I would aim for. Design the API you can imagine keeping
for ten years. Make it simple underneath, even when that costs more than making it easy. &lt;strong&gt;Build it so it
can outgrow you.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I tried to do the same thing a few years later on a compiler, which is the story of
&lt;a href=&quot;https://manualmeida.dev/articles/qwik-resumability&quot;&gt;Qwik&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://manualmeida.dev/articles/gin-simple-over-easy&quot;&gt;This article has interactive figures — read it on manualmeida.dev.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Open Source</category><category>go</category><category>performance</category><category>http</category><category>open source</category></item><item><title>Half-Truths</title><link>https://manualmeida.dev/articles/notes-i-keep/</link><guid isPermaLink="true">https://manualmeida.dev/articles/notes-i-keep/</guid><description>A text file of one-liners I&apos;ve muttered at a terminal over the years. Half of them are jokes and several contradict each other on purpose. None of them are advice.</description><pubDate>Tue, 28 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;On writing as little as possible&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Software is best without classes, or functions, or code. But if you must, write as little as possible.&lt;/li&gt;
&lt;li&gt;The only thing that really matters in software is KISS.&lt;/li&gt;
&lt;li&gt;That is classic over-abstraction.&lt;/li&gt;
&lt;li&gt;When in doubt, add &lt;code&gt;setTimeout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No hack is complete without &lt;code&gt;.replace()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; is for cowards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On shipping&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Never merge a PR.&lt;/li&gt;
&lt;li&gt;If you’re afraid to release on Friday, the problem isn’t Friday.&lt;/li&gt;
&lt;li&gt;If you can’t release fast, you have tech debt to solve.&lt;/li&gt;
&lt;li&gt;Precommit hooks are the root of all evil.&lt;/li&gt;
&lt;li&gt;The difference between a hobby and a job is that you don’t write documentation for the former.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On things one if-statement away from disaster&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Almost all software is one if-statement away from disaster.&lt;/li&gt;
&lt;li&gt;If you code just to make things work, one day suddenly nothing will work.&lt;/li&gt;
&lt;li&gt;Dependencies are the root of all evil.&lt;/li&gt;
&lt;li&gt;Developers will always find unexpected ways to use your API.&lt;/li&gt;
&lt;li&gt;There are two hard things in computer science: cache invalidation and symlinks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;On knowing better&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Debugging skills are underrated.&lt;/li&gt;
&lt;li&gt;You are only an expert in a given technology when you know when &lt;em&gt;not&lt;/em&gt; to use it.&lt;/li&gt;
&lt;li&gt;A good engineer balances not underestimating apparently simple tasks against not overengineering
apparently complex ones.&lt;/li&gt;
&lt;li&gt;An experienced developer is the one who knows when to be volatile and when to be stable. (I figured
it out, by the way: a stable is someone who installs linting build scripts, and a volatile is
someone who disables them.)&lt;/li&gt;
&lt;li&gt;I love monorepos, but I hate lerna.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;If you got this far looking for a coherent philosophy, I’m sorry. The honest version is that good
engineering is mostly knowing which of these to ignore today.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: a good number of these were forged in long, unhinged rants with
&lt;a href=&quot;https://x.com/adamdbradley&quot;&gt;Adam Bradley&lt;/a&gt;. I’ll take half the blame and none of the credit.&lt;/em&gt;&lt;/p&gt;
</content:encoded><dc:creator>Manu Martínez-Almeida</dc:creator><category>Notes</category><category>notes</category><category>opinions</category><category>humor</category></item></channel></rss>