<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patu</title>
    <description>The latest articles on DEV Community by Patu (@patu).</description>
    <link>https://dev.to/patu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4035343%2F66c45d02-b61d-42c9-bd99-bd2527ab0c08.png</url>
      <title>DEV Community: Patu</title>
      <link>https://dev.to/patu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/patu"/>
    <language>en</language>
    <item>
      <title>Keep an ER diagram of your database, updated on every push</title>
      <dc:creator>Patu</dc:creator>
      <pubDate>Tue, 01 Sep 2026 17:33:23 +0000</pubDate>
      <link>https://dev.to/patu/keep-an-er-diagram-of-your-database-updated-on-every-push-3994</link>
      <guid>https://dev.to/patu/keep-an-er-diagram-of-your-database-updated-on-every-push-3994</guid>
      <description>&lt;p&gt;Paste a SQL schema, get an interactive entity-relationship diagram on a stable link. Wire it into CI and the diagram regenerates itself every time the schema changes: no diagram to draw, ever again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpa1vcwc4ium0moox3isr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpa1vcwc4ium0moox3isr.webp" alt="The Chinook schema as one explorable page" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with hand-drawn diagrams
&lt;/h2&gt;

&lt;p&gt;A database diagram is out of date the moment someone adds a column. You draw it once for the onboarding doc, and three migrations later it lies.&lt;/p&gt;

&lt;p&gt;mcdview takes the opposite route: the schema file &lt;em&gt;is&lt;/em&gt; the source of truth. Point it at your &lt;code&gt;schema.sql&lt;/code&gt; and it generates the diagram: a single self-contained HTML page, no runtime, no database connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the diagram does
&lt;/h2&gt;

&lt;p&gt;Every table and relationship is interactive: click a table to isolate it, highlight the hub tables everything depends on, surface dependency cycles, flip between light and dark. You can export the same model as SVG, a Mermaid &lt;code&gt;erDiagram&lt;/code&gt;, or a Markdown data dictionary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv48grd8u3mhc78pqce.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv48grd8u3mhc78pqce.webp" alt="Isolating a table" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it in CI: the diagram updates itself
&lt;/h2&gt;

&lt;p&gt;Your schema lives in the repo. On every push that touches it, CI sends the file to mcdview.dev, which regenerates the diagram &lt;strong&gt;behind the same stable link&lt;/strong&gt;. Bookmark it once; it is always current.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;er-diagram&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;db/schema.sql&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gheop/mcdview-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db/schema.sql&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MCDVIEW_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitLab CI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitlab.com/Gheop/mcdview/mcdview@~latest&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db/schema.sql&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$MCDVIEW_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No secret to manage? Use &lt;code&gt;oidc: true&lt;/code&gt; instead of a key: the pipeline proves its own identity with a signed token, and the diagram is bound to that project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitlab.com/Gheop/mcdview/mcdview@~latest&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db/schema.sql&lt;/span&gt;
      &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A badge for your README
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;![mcdview&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://mcdview.dev/badge/&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;token&amp;gt;&lt;/span&gt;.svg)](https://mcdview.dev/v/&lt;span class="nt"&gt;&amp;lt;token&amp;gt;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj40sbxg9gtd6xisrkmrq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj40sbxg9gtd6xisrkmrq.webp" alt="Diff between two versions" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different
&lt;/h2&gt;

&lt;p&gt;Three things you rarely get in one tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One file, no account.&lt;/strong&gt; Paste a schema, get a link. The page is a single static HTML file: nothing runs when you open it, no database is contacted, nothing to sign up for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It lives in CI.&lt;/strong&gt; A GitHub Action or GitLab component regenerates the diagram on every push, behind the same stable link, with a README badge. Zero-key with OIDC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It remembers.&lt;/strong&gt; Every push is a version: scrub the time-lapse, diff any two states, see what a migration changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that: isolate a table, spot hub tables and cycles, switch light or dark, and export to SVG, Mermaid or a Markdown data dictionary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in 30 seconds
&lt;/h2&gt;

&lt;p&gt;No sign-up. &lt;strong&gt;&lt;a href="https://mcdview.dev" rel="noopener noreferrer"&gt;Open mcdview.dev&lt;/a&gt;&lt;/strong&gt;. Supports PostgreSQL, MySQL, SQLite dumps, pgModeler &lt;code&gt;.dbm&lt;/code&gt;, DBML and Prisma.&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>devops</category>
      <category>ci</category>
    </item>
    <item>
      <title>Turn any SQL schema into an interactive ER diagram</title>
      <dc:creator>Patu</dc:creator>
      <pubDate>Sun, 30 Aug 2026 11:29:06 +0000</pubDate>
      <link>https://dev.to/patu/turn-any-sql-schema-into-an-interactive-er-diagram-3125</link>
      <guid>https://dev.to/patu/turn-any-sql-schema-into-an-interactive-er-diagram-3125</guid>
      <description>&lt;p&gt;Reading a database you did not design usually starts the same way: you open the schema, scroll through a few hundred lines of &lt;code&gt;CREATE TABLE&lt;/code&gt;, and try to hold the foreign keys in your head. A picture would help, but drawing one by hand goes stale the moment the schema changes.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://mcdview.dev/" rel="noopener noreferrer"&gt;mcdview&lt;/a&gt; to skip that step. You drop a schema file and you get back a single self-contained HTML page: an interactive entity-relationship diagram you can zoom, search, isolate one table with its neighbours, and share by link. No account, nothing to install.&lt;/p&gt;

&lt;p&gt;Here is one generated from the Pagila sample database, so you can see the output before reading further: &lt;a href="https://mcdview.dev/v/2slcbFWyTvqJvIAxorPlSQ" rel="noopener noreferrer"&gt;mcdview.dev/v/2slcbFWyTvqJvIAxorPlSQ&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it reads
&lt;/h2&gt;

&lt;p&gt;The point was to take whatever you already have, not to make you export to a specific format first. So it accepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL&lt;/strong&gt;: PostgreSQL, MySQL/MariaDB, SQLite and about 15 dialects, detected automatically. A plain &lt;code&gt;pg_dump&lt;/code&gt; works. Several &lt;code&gt;.sql&lt;/code&gt; files at once work too, for a schema split across files, with foreign keys resolved across them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pgModeler&lt;/strong&gt; &lt;code&gt;.dbm&lt;/code&gt;, &lt;strong&gt;MySQL Workbench&lt;/strong&gt; &lt;code&gt;.mwb&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dbml&lt;/strong&gt; (dbdiagram.io), &lt;strong&gt;Prisma&lt;/strong&gt;, &lt;strong&gt;Rails&lt;/strong&gt; &lt;code&gt;schema.rb&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mermaid&lt;/strong&gt; erDiagram, &lt;strong&gt;Drizzle ORM&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The page you get
&lt;/h2&gt;

&lt;p&gt;The generated page is static HTML with no runtime dependency, so it is safe to host anywhere or open straight from disk. The diagram itself is interactive: pan and zoom, a search box, click a table to isolate it with the tables it references, a minimap for large models, light and dark themes. Audit-trail columns like &lt;code&gt;created_by&lt;/code&gt; or &lt;code&gt;modified_by&lt;/code&gt; are detected and folded away by default so they stop cluttering the graph, with a toggle to bring them back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it from CI
&lt;/h2&gt;

&lt;p&gt;There is a small HTTP API. A create call returns a public link and a management key; an update call regenerates the diagram under the same link, so you can keep one stable URL in your README and refresh it on every build. From the second version on, the update also returns a diff between versions (tables and indexes added, changed, removed). You can also pull the Mermaid &lt;code&gt;erDiagram&lt;/code&gt; as plain text to paste into a GitHub or GitLab README, which both render natively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;The generator is MIT-licensed: &lt;a href="https://github.com/Gheop/mcdview" rel="noopener noreferrer"&gt;github.com/Gheop/mcdview&lt;/a&gt;. It is a self-contained Python script, so you can run it locally or in your own pipeline instead of the hosted service if you prefer.&lt;/p&gt;

&lt;p&gt;Try it on your own schema: &lt;a href="https://mcdview.dev/" rel="noopener noreferrer"&gt;mcdview.dev&lt;/a&gt;. Feedback welcome, especially on dialects or formats that trip it up.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>postgres</category>
      <category>tools</category>
    </item>
    <item>
      <title>Don't trust your SVG optimizer. Render-diff its output.</title>
      <dc:creator>Patu</dc:creator>
      <pubDate>Sat, 18 Jul 2026 13:46:53 +0000</pubDate>
      <link>https://dev.to/patu/dont-trust-your-svg-optimizer-render-diff-its-output-4bl9</link>
      <guid>https://dev.to/patu/dont-trust-your-svg-optimizer-render-diff-its-output-4bl9</guid>
      <description>&lt;p&gt;SVG optimizers are wonderful right up to the day one quietly breaks an icon.&lt;/p&gt;

&lt;p&gt;Run SVGO (or any optimizer) across a big icon set and you'll eventually hit it: a &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt; gets collapsed, a path gets rewritten into a mathematically "equivalent" form, and in most renderers it looks identical, but in one specific renderer, or at one specific size, it's subtly wrong. A shape that was outlined is now filled. A stroke disappears. You don't notice until it's in production, because it didn't show up on the five icons you spot-checked.&lt;/p&gt;

&lt;p&gt;Here's the uncomfortable part: most optimizers guarantee "smaller," not "renders the same." Those are different promises. An optimizer can shave bytes and, on an edge case, change what the file draws. The transforms are usually safe. "Usually" is the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix isn't a better optimizer. It's verification.
&lt;/h2&gt;

&lt;p&gt;Instead of trusting that a transform preserved rendering, check it. After optimizing, rasterize the original SVG and the optimized SVG, compare the two bitmaps, and if they diverge beyond a small tolerance, throw the optimized version away and fall back.&lt;/p&gt;

&lt;p&gt;The guarantee flips from "these transforms are provably safe" (hard, and false in the edge cases) to "the pixels didn't change" (easy to check, and exactly what you care about).&lt;/p&gt;

&lt;p&gt;A sketch of the idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c"&gt;// your SVG minifier of choice&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rasterize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c"&gt;// longest edge 512px is plenty&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rasterize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sameSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;diffFraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0.02&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                      &lt;span class="c"&gt;// pixels match: trust it&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;       &lt;span class="c"&gt;// diverged: don't ship it&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things that matter in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flatten onto an opaque background before comparing.&lt;/strong&gt; A shape that lost its fill and fell back to transparent should count as a difference, not get masked by alpha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow a small per-channel tolerance&lt;/strong&gt; (say 16/255). Anti-aliasing shifts sub-pixels when path data is rewritten; that's fine. A filled-vs-unfilled shape is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A small render is enough.&lt;/strong&gt; You're catching gross divergence (a dropped stroke, a flipped fill), not fine detail, so a 512px raster keeps the check cheap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You render twice, which isn't free, but for build-time optimization of an icon set you pay it once and never ship a broken icon.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools
&lt;/h2&gt;

&lt;p&gt;The optimizer I use is &lt;strong&gt;silk&lt;/strong&gt;, a pure-Go SVG optimizer in the spirit of SVGO (path data, structural passes). It ships as a single static binary, no Node toolchain, which is why it drops into a Go service or a minimal CI image where a JS toolchain would be a pain. Like most optimizers, it guarantees smaller, not identical, so the render-diff above is the layer I put around it: it lives in &lt;a href="https://patu.dev" rel="noopener noreferrer"&gt;Patu&lt;/a&gt;, the asset service these came out of, and falls back to a conservative minifier whenever the render moved.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;silk (MIT): &lt;a href="https://github.com/Gheop/silk" rel="noopener noreferrer"&gt;https://github.com/Gheop/silk&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a sibling for fonts, &lt;strong&gt;woffify&lt;/strong&gt;: TTF/OTF/WOFF to WOFF2 plus glyph subsetting (via HarfBuzz), one binary. A raw TTF shipped instead of a subsetted WOFF2 is usually two-thirds-plus wasted bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;woffify &lt;span class="nt"&gt;-subset-text&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;page.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;lt; Inter.ttf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Inter.woff2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;woffify (MIT): &lt;a href="https://github.com/Gheop/woffify" rel="noopener noreferrer"&gt;https://github.com/Gheop/woffify&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you optimize SVGs at scale, steal the render-diff idea even if you never touch these tools. It's the cheapest insurance against the one broken icon you won't catch by eye.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>performance</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
