<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="/rss.xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech on Philipp D. Dubach | Quantitative Finance &amp; AI Strategy</title><link>https://philippdubach.com/categories/tech/</link><description>Recent content in Tech on Philipp D. Dubach | Quantitative Finance &amp; AI Strategy</description><image><url>https://static.philippdubach.com/ograph/ograph-post.jpg</url><title>Philipp D. Dubach | Quantitative Finance &amp; AI Strategy</title><link>https://philippdubach.com/</link></image><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>me@philippdubach.com (Philipp D. Dubach)</managingEditor><webMaster>me@philippdubach.com (Philipp D. Dubach)</webMaster><atom:link href="https://philippdubach.com/categories/tech/index.xml" rel="self" type="application/rss+xml"/><item><title>Karpathy's Software 3.0 Playbook</title><link>https://philippdubach.com/posts/karpathys-software-3.0-playbook/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/karpathys-software-3.0-playbook/</guid><description>&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-karpathy_header-png-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/karpathy_header.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/karpathy_header.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/karpathy_header.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/karpathy_header.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/karpathy_header.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/karpathy_header.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/karpathy_header.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/karpathy_header.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/karpathy_header.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/karpathy_header.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/karpathy_header.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/karpathy_header.png"
alt="Still from Andrej Karpathy&amp;#39;s interview with Sequoia at AI Ascent discussing Software 3.0, vibe coding, and agentic engineering"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-karpathy_header-png-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/karpathy_header.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Still from Andrej Karpathy&amp;#39;s interview with Sequoia at AI Ascent discussing Software 3.0, vibe coding, and agentic engineering" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;Andrej Karpathy is one of the few people who has both built modern AI and explained it for the rest of us. He co-founded OpenAI, ran computer vision at Tesla (where he got Autopilot working), and his courses on neural networks are some of the most-watched lectures on the internet. He also has a habit of naming the era we&amp;rsquo;re already in. &amp;ldquo;Vibe coding&amp;rdquo; was his. &amp;ldquo;Software 3.0&amp;rdquo; looks like the next one.&lt;/p&gt;
&lt;p&gt;So when Karpathy says he has &amp;ldquo;never felt more behind as a programmer,&amp;rdquo; it is worth slowing down. That isn&amp;rsquo;t false modesty from a guy with his résumé. Something shifted under the field and most people haven&amp;rsquo;t recalibrated. The Sequoia interview below is his attempt to describe what shifted. The lessons here are pulled from it, ordered roughly by how much they should change what you do tomorrow.&lt;/p&gt;
&lt;h2 id="1-inflection-point-december-2025"&gt;1. Inflection point December 2025&lt;/h2&gt;
&lt;p&gt;Until late last year, agentic coding tools were &amp;ldquo;kind of helpful.&amp;rdquo; Good in stretches, often wrong in ways you had to babysit. Over the December break, the latest models crossed a line:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I kept asking for more and just came out fine. And then I can&amp;rsquo;t remember the last time I corrected it. And then I just trusted the system more and more. And then I was vibe coding.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He flagged it on the record, loudly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;A lot of people experienced AI last year as a ChatGPT-adjacent thing. But you really had to look again, and you had to look as of December, because things have changed fundamentally — especially on this agentic, coherent workflow that really started to actually work.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If your mental model of these tools was set by ChatGPT, it is already a generation stale. The agentic workflow is a different product, and it now works.&lt;/p&gt;
&lt;h2 id="2-you-can-outsource-your-thinking-but-not-your-understanding"&gt;2. You can outsource your thinking, but not your understanding&lt;/h2&gt;
&lt;p&gt;The most quotable line of the interview:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;You can outsource your thinking, but you can&amp;rsquo;t outsource your understanding.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As agents do more of the thinking, the bottleneck moves into your head. You still have to know what is worth building and why, and you still have to direct the work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;m still part of the system, and I still have to — somehow, information still has to make it into my brain. And I feel like I&amp;rsquo;m becoming a bottleneck of just even knowing what are we trying to build, why is it worth doing, how do I direct my agents.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Your value sits upstream of execution. The bottleneck of the next decade is less about compute than about how fast humans can deepen comprehension to keep directing systems that out-execute them. That is why Karpathy keeps building knowledge bases out of his own reading. He wants another projection of the same data, faster.&lt;/p&gt;
&lt;h2 id="3-verifiability-is-the-map-of-what-automates-next"&gt;3. Verifiability is the map of what automates next&lt;/h2&gt;
&lt;p&gt;Why are these models freakishly good at code and math, and yet stupid about whether you should walk 50 meters to a car wash? Because frontier labs train via reinforcement learning, and RL needs verifiable rewards. Verifiable domains attract environments and signal, so they get the steepest gains. Everything else stays jagged.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;How is it possible that state-of-the-art Opus 4.7 will simultaneously refactor a hundred-thousand-line code base or find zero-day vulnerabilities, and yet tells me to walk to this car wash? This is insane.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The GPT-3.5 to GPT-4 chess jump is the proof point. Capability tracks what the labs choose to feed in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;We are slightly at the mercy of whatever the labs are doing, whatever they happen to put into the mix&amp;hellip; If you&amp;rsquo;re in the circuits that were part of the RL, you fly. And if you&amp;rsquo;re in the circuits that are out of the data distribution, you&amp;rsquo;re going to struggle.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two things follow. If you are a founder and you can build a verifiable environment in your domain, even one the labs aren&amp;rsquo;t focused on, you can fine-tune a model that flies. That is real leverage. If you are a worker, the more useful question than &amp;ldquo;is my job safe?&amp;rdquo; is &amp;ldquo;is my job verifiable?&amp;rdquo; Karpathy thinks everything is automatable eventually. Verifiability mainly sets the order.&lt;/p&gt;
&lt;h2 id="4-software-30-prompting-is-the-new-programming"&gt;4. Software 3.0: prompting is the new programming&lt;/h2&gt;
&lt;p&gt;The frame that makes the rest of this make sense. Karpathy&amp;rsquo;s three eras:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Software 1.0:&lt;/strong&gt; humans write explicit code.&lt;br&gt;
&lt;strong&gt;Software 2.0:&lt;/strong&gt; humans curate datasets and train neural networks; the weights are the program.&lt;br&gt;
&lt;strong&gt;Software 3.0:&lt;/strong&gt; humans write prompts; the LLM is the interpreter, and the context window is the program.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Your programming now turns to prompting. And what&amp;rsquo;s in the context window is over the interpreter, that is the LLM, that is kind of like interpreting your context and performing computation in the digital information space.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;His sharpest example: installing OpenCode is no longer a shell script. It is a block of text you copy-paste to your agent, which reads your environment and figures the rest out.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;It&amp;rsquo;s just like, what is the piece of text to copy-paste to your agent? That&amp;rsquo;s the programming paradigm now.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The unit of programming used to be a function. Now it is closer to a paragraph.&lt;/p&gt;
&lt;h2 id="5-vibe-coding-raises-the-floor-agentic-engineering-raises-the-ceiling"&gt;5. Vibe coding raises the floor; agentic engineering raises the ceiling&lt;/h2&gt;
&lt;p&gt;If you build software for a living, this is the lesson with the most direct implications:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Vibe coding is about raising the floor for everyone in terms of what they can do in software&amp;hellip; But agentic engineering is about preserving the quality bar of what existed before in professional software. You&amp;rsquo;re still responsible for your software just as before, but can you go faster?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Karpathy thinks the ceiling is very high:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;People used to talk about the 10x engineer previously. I think that this is magnified a lot more — 10x is not the speed up you gain. People who are very good at this peak a lot more than 10x.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The gap between mediocre and excellent users of these tools is widening. Worth taking seriously when you decide what to learn next.&lt;/p&gt;
&lt;h2 id="6-the-new-human-skill-is-taste-spec-and-oversight"&gt;6. The new human skill is taste, spec, and oversight&lt;/h2&gt;
&lt;p&gt;What humans should still do, in his telling, is design and judgment work. Holding the spec in your head. Setting the architecture. Making sure the agent is being asked for the right thing in the first place.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;You&amp;rsquo;re in charge of the taste, the engineering, the design, and that it makes sense, and that you&amp;rsquo;re asking for the right things&amp;hellip; You&amp;rsquo;re doing some of the design and development, and the engineers are doing the fill in the blanks.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The MenuGen anecdote is the kind of mistake only a human spec catches. The agent silently tried to associate Stripe and Google accounts by matching email addresses, with no persistent user ID. It worked until two emails diverged.&lt;/p&gt;
&lt;p&gt;He is not sure this division will hold forever:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;When you actually look at the code, sometimes I get a little bit of a heart attack, because it&amp;rsquo;s not super amazing code&amp;hellip; It&amp;rsquo;s very bloaty, and there&amp;rsquo;s a lot of copy-paste, and there&amp;rsquo;s awkward abstractions that are brittle and — like, it works, but it&amp;rsquo;s just really gross.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nothing fundamental stops the labs from training for taste. They just haven&amp;rsquo;t yet. Until they do, the taste layer is still your responsibility.&lt;/p&gt;
&lt;h2 id="7-some-apps-shouldnt-exist-anymore"&gt;7. Some apps shouldn&amp;rsquo;t exist anymore&lt;/h2&gt;
&lt;p&gt;The MenuGen anecdote, again. Karpathy built an app: photo a restaurant menu, OCR it, generate images of each dish, render a new menu. Vercel deployment, the full stack.&lt;/p&gt;
&lt;p&gt;Then he saw the Software 3.0 version. Hand the photo to Gemini, say &amp;ldquo;use NanoBanana to overlay the dishes onto the menu,&amp;rdquo; and a single model call returns the same menu with images rendered into the pixels.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;All of my MenuGen is spurious. It&amp;rsquo;s working in the old paradigm. That app shouldn&amp;rsquo;t exist.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A lot of what we are building today is scaffolding around a capability the model could perform end-to-end. Before writing the next CRUD app, ask whether the model is the app.&lt;/p&gt;
&lt;h2 id="8-new-possibilities-matter-more-than-the-speed-ups"&gt;8. New possibilities matter more than the speed-ups&lt;/h2&gt;
&lt;p&gt;The flip side of &amp;ldquo;some apps shouldn&amp;rsquo;t exist&amp;rdquo; is that some products could not have existed before. Karpathy&amp;rsquo;s knowledge-base project is the example. Take a pile of documents, ask the LLM to recompile them into a wiki, surface the connections you would never have stitched together by hand.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;This is not even a program. This is not something that could exist before, because there was no code that would create a knowledge base based on a bunch of facts. But now you can just take these documents and basically recompile them in a different way&amp;hellip; I almost think that that&amp;rsquo;s more exciting.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you only ask what gets faster, you will miss the more interesting question, which is what becomes possible at all.&lt;/p&gt;
&lt;h2 id="9-jagged-intelligence-ghosts-not-animals"&gt;9. Jagged intelligence: ghosts, not animals&lt;/h2&gt;
&lt;p&gt;Karpathy&amp;rsquo;s metaphor: we are not building animals. Animal intelligence comes with intrinsic motivation, embodiment, drives shaped by evolution. What we have instead is more like a ghost. A statistical simulator shaped by pre-training, with RL bolted on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;These things are not animal intelligences. Like, if you yell at them, they&amp;rsquo;re not going to work better. Or worse. Or it doesn&amp;rsquo;t have any impact. And it&amp;rsquo;s all just kind of these statistical simulation circuits where the substrate is pre-training. So, statistics. And then there&amp;rsquo;s RL bolting on top.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The practical takeaway is to stop reasoning about LLMs by analogy to humans. Be suspicious of where the model seems confident, probe the edges, and figure out which circuits your task is actually landing in.&lt;/p&gt;
&lt;h2 id="10-build-agent-native-infrastructure"&gt;10. Build agent-native infrastructure&lt;/h2&gt;
&lt;p&gt;For infra builders, Karpathy&amp;rsquo;s pet peeve is also the opportunity:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Why are people still telling me what to do? Like, I don&amp;rsquo;t want to do anything. What is the thing I should copy-paste to my agent?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rebuild the developer stack so the primary consumer of docs, configs, APIs, and deployment flows is an agent rather than a human. Data structures should be legible to LLMs by default, and sensors and actuators over the world should sit behind agent-callable interfaces.&lt;/p&gt;
&lt;p&gt;His test: can you say &amp;ldquo;build and deploy MenuGen&amp;rdquo; and never touch a settings panel? When the answer is yes, the infrastructure has caught up.&lt;/p&gt;
&lt;h2 id="11-hire-for-big-projects-not-puzzles"&gt;11. Hire for big projects, not puzzles&lt;/h2&gt;
&lt;p&gt;A direct shot at hiring managers. Most companies have not refactored their interview loops for the agentic era.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Hiring has to look like, give me a really big project and see someone implement that big project. Like, let&amp;rsquo;s write, say, a Twitter clone for agents, and then make it really good, make it really secure, and then have some agents simulate some activity on this Twitter. And then I&amp;rsquo;m going to use 10 Codex 5.4-X-high to try to break your website.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whiteboard puzzles measure the wrong thing. If your interview loop has not changed since 2022, you are selecting for the previous era.&lt;/p&gt;
&lt;h2 id="12-imagine-the-weird-endpoint"&gt;12. Imagine the weird endpoint&lt;/h2&gt;
&lt;p&gt;The closing speculation is genuinely strange:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;In the early days of computing, people were a little bit confused as to whether computers would look like calculators or computers would look like neural nets. And in the &amp;rsquo;50s and &amp;rsquo;60s, it was not really obvious which way it would go&amp;hellip; You could imagine that a lot of this will flip and that the neural net becomes kind of the host process, and the CPUs become kind of the coprocessor.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;UIs diffusion-rendered moment by moment from raw video and audio. No apps in between.&lt;/p&gt;
&lt;p&gt;You do not have to buy this exact picture. The point is simply that the linear extrapolation, the same software but smarter, is almost certainly the wrong frame for where this ends up.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Based on Andrej Karpathy&amp;rsquo;s &lt;a href="https://www.youtube.com/watch?v=96jN2OCOfLs"&gt;interview with Sequoia&lt;/a&gt; at AI Ascent&lt;/em&gt;&lt;/p&gt;</description></item><item><title>On-Device AI Models Will Be The New Reason to Upgrade Your Phone</title><link>https://philippdubach.com/posts/on-device-ai-models-will-be-the-new-reason-to-upgrade-your-phone/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/on-device-ai-models-will-be-the-new-reason-to-upgrade-your-phone/</guid><description>&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-chip-cover-jpg-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/chip-cover.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/chip-cover.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/chip-cover.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/chip-cover.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/chip-cover.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/chip-cover.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/chip-cover.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/chip-cover.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/chip-cover.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/chip-cover.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/chip-cover.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/chip-cover.jpg"
alt="Editorial cover illustration for an analysis of on-device AI models as the new smartphone upgrade driver"
class=""
width="1200"
fetchpriority="high"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-chip-cover-jpg-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/chip-cover.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Editorial cover illustration for an analysis of on-device AI models as the new smartphone upgrade driver" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;The iPhone 17 runs a &lt;a href="https://machinelearning.apple.com/research/introducing-apple-foundation-models"&gt;3 billion parameter language model on-device&lt;/a&gt; at 30 tokens per second. Obviously, the average consumer has no idea what that sentence means, and Apple hasn&amp;rsquo;t figured out how to make them care.&lt;/p&gt;
&lt;p&gt;I believe that&amp;rsquo;s about to change. Apple now has &lt;a href="https://9to5mac.com/2026/03/25/new-details-on-apple-google-ai-deal-revealed-including-gemini-changes-report/"&gt;complete access to Google&amp;rsquo;s Gemini model&lt;/a&gt; in its own data centers, with &lt;a href="https://www.theinformation.com/newsletters/ai-agenda/apple-can-distill-googles-big-gemini-model"&gt;the ability to distill it into smaller models&lt;/a&gt; built for iPhones and iPads. Knowledge distillation works like this: you take a large model, have it perform tasks with detailed reasoning, then feed those reasoning traces to a smaller model until the student learns to mimic the teacher. The smaller model ends up far more capable than if you&amp;rsquo;d trained it from scratch on the same data. Apple can now do this with the full Gemini, not just their own in-house models, and the distilled output runs locally. No internet required.&lt;/p&gt;
&lt;p&gt;Smartphones haven&amp;rsquo;t had a real upgrade story in years. The camera is great. The screen is great. The processor was fast enough three generations ago. &lt;a href="https://www.sellcell.com/blog/how-often-do-people-upgrade-their-phone/"&gt;Battery life has overtaken price as the top purchase driver&lt;/a&gt; for the first time. The global &lt;a href="https://sqmagazine.co.uk/smartphone-statistics/"&gt;replacement cycle has stretched to 3.5 years&lt;/a&gt;. People hold onto their phones because nothing about the new one feels different enough. &lt;a href="https://www.deloitte.com/us/en/insights/industry/technology/technology-media-and-telecom-predictions/2025/gen-ai-on-smartphones.html"&gt;Deloitte&amp;rsquo;s 2025 TMT Predictions report&lt;/a&gt; frames on-device generative AI as the feature that could break this cycle, if the experience delivers on the promise. On-device AI might become the next reason.&lt;/p&gt;
&lt;h2 id="the-spec"&gt;The spec&lt;/h2&gt;
&lt;p&gt;In the late 1990s it was megahertz: Intel and AMD raced clock speeds past the point where consumers could distinguish real-world performance differences, but the number on the box still drove purchases. Then it was megapixels. Samsung shipped a &lt;a href="https://semiconductor.samsung.com/news-events/tech-blog/isocell-hp3-200mp-image-sensor-for-epic-details/"&gt;200 MP camera sensor&lt;/a&gt; knowing that most phones use 16-to-1 pixel binning to output a &lt;strong&gt;12.5 MP&lt;/strong&gt; image by default.&lt;/p&gt;
&lt;p&gt;Parameters could be next. The &lt;a href="https://www.apple.com/iphone-17/specs/"&gt;iPhone 17&amp;rsquo;s standard A19 chip&lt;/a&gt; has 8GB of RAM. The &lt;a href="https://www.apple.com/iphone-17-pro/specs/"&gt;Pro gets 12GB&lt;/a&gt; with faster memory bandwidth, which determines how large a model the phone can run and how quickly. Samsung&amp;rsquo;s 2026 flagships with the &lt;a href="https://semiconductor.samsung.com/processor/mobile-processor/exynos-2600/"&gt;Exynos 2600 hit &lt;strong&gt;80 TOPS&lt;/strong&gt;&lt;/a&gt; on a 2nm process, more than double the prior generation. These are already the numbers in press releases. It&amp;rsquo;s not hard to imagine an Apple keynote where someone says, with rehearsed enthusiasm, that the iPhone 18 Pro runs a 7 billion parameter model while the standard model is limited to 3 billion.&lt;/p&gt;
&lt;p&gt;The difference from previous spec wars is that this one might actually correlate with user experience. Megahertz past a certain threshold didn&amp;rsquo;t make Word open faster. Megapixels past 12 MP didn&amp;rsquo;t make photos look better on a phone screen. But a 7 billion parameter model running locally outperforms a 3 billion one on nearly every task. It handles longer documents, follows more complex instructions, holds better conversational context.&lt;/p&gt;
&lt;h2 id="breaking-the-stalemate"&gt;Breaking the stalemate&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.gartner.com/en/newsroom/press-releases/2025-09-09-gartner-says-worldwide-generative-artificial-intelligence-smartphone-end-user-spending-to-total-us-dollars-298-billion-by-the-end-of-2025"&gt;Gartner projects&lt;/a&gt; GenAI smartphone spending will reach &lt;strong&gt;$393 billion&lt;/strong&gt; in 2026, up 32% from &lt;strong&gt;$298 billion&lt;/strong&gt; in 2025. &lt;a href="https://my.idc.com/getdoc.jsp?containerId=prUS52478124"&gt;IDC reports&lt;/a&gt; GenAI smartphone shipments growing &lt;strong&gt;73%&lt;/strong&gt; year over year. &lt;a href="https://finance.yahoo.com/news/exclusive-samsung-double-mobile-devices-030312758.html"&gt;Samsung has publicly committed&lt;/a&gt; to 800 million AI-enabled devices by end of 2026, doubling its 2025 footprint. &lt;a href="https://www.cnbc.com/2024/12/13/apple-is-a-top-pick-for-2025-as-ai-will-drive-iphone-upgrade-cycle-morgan-stanley-says.html"&gt;Morgan Stanley&amp;rsquo;s latest survey&lt;/a&gt; found iPhone upgrade intentions at &lt;strong&gt;37%&lt;/strong&gt;, an all-time high, with FY26 shipment forecasts of 260 million units sitting 3% above Street consensus.&lt;/p&gt;
&lt;p&gt;On-device AI creates hard hardware requirements in a way that camera improvements and screen upgrades never did. You cannot run a 3 billion parameter model on an iPhone 14. The Neural Engine isn&amp;rsquo;t powerful enough and the memory bandwidth isn&amp;rsquo;t there. &lt;a href="https://support.apple.com/en-us/121115"&gt;Apple Intelligence requires an A17 Pro or later&lt;/a&gt;, which means the feature itself creates an upgrade floor. Every year that floor rises. When Apple ships distilled Gemini models that need the A19 Pro&amp;rsquo;s 12GB of RAM, every phone older than 2025 is locked out.&lt;/p&gt;
&lt;p&gt;The Gemini deal matters for the hardware cycle because of the distillation pipeline. Apple doesn&amp;rsquo;t need to build frontier-scale models from scratch. They can take Gemini&amp;rsquo;s best capabilities, run them through distillation, and compress the results into models sized for their hardware tiers. A 3 billion parameter model for the standard iPhone. A 5 billion version for the Pro. Maybe a 10 billion model for a future iPad Pro with enough memory and thermal headroom.&lt;/p&gt;
&lt;p&gt;Google is playing a similar game from the other side. The original &lt;a href="https://en.wikipedia.org/wiki/Gemini_(language_model)"&gt;Gemini Nano shipped at 1.8 billion parameters&lt;/a&gt;; the updated Nano-2 rose to 3.25 billion. Samsung&amp;rsquo;s &lt;a href="https://news.samsung.com/global/samsung-unveils-galaxy-s26-series-the-most-intuitive-galaxy-ai-phone-yet"&gt;Galaxy S26 ships with on-device Gemini&lt;/a&gt; running on NPUs that are 39% faster than the prior generation. On-device models get larger every hardware generation. Each generation&amp;rsquo;s models don&amp;rsquo;t run well on older hardware. You see where this goes.&lt;/p&gt;
&lt;p&gt;I find it plausible that within two product cycles, on-device model capability becomes the primary differentiator between phone tiers and between generations. The data isn&amp;rsquo;t there yet: &lt;a href="https://www.twice.com/research/the-smartphone-upgrade-cycle-slows"&gt;only 17% of Americans&lt;/a&gt; say AI is a major purchase influence today, Apple Intelligence &lt;a href="https://finance.yahoo.com/markets/stocks/articles/morgan-stanley-stark-message-investors-164700952.html"&gt;ranked seventh globally&lt;/a&gt; as a reason to upgrade in Morgan Stanley&amp;rsquo;s survey, and &lt;a href="https://www.phonearena.com/news/is-the-ai-boom-destroying-your-next-flagship-phones-value_id176913"&gt;over 40% of users&lt;/a&gt; have privacy concerns about smartphone AI, with half unwilling to pay extra for it. But you can&amp;rsquo;t tell the difference between a 48 MP photo and a 12 MP photo on your phone screen. You can absolutely tell the difference between an AI assistant that understands your question and one that doesn&amp;rsquo;t. The feedback loop is immediate and personal. If the bigger model actually works better, and if the distillation pipeline from Gemini delivers real capability gains, the upgrade incentive is self-reinforcing. People will upgrade not because the spec sheet says they should, but because they tried their friend&amp;rsquo;s phone and the AI was better.&lt;/p&gt;
&lt;p&gt;Whether this arrives with iOS 27 this fall or takes another generation to mature, I don&amp;rsquo;t know. But the next reason to buy a new phone will much more likely be the model than the camera.&lt;/p&gt;</description></item><item><title>The Last Architecture Designed by Hand</title><link>https://philippdubach.com/posts/the-last-architecture-designed-by-hand/</link><pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/the-last-architecture-designed-by-hand/</guid><description>&lt;blockquote&gt;
&lt;p&gt;I bet there is another new architecture to find that is gonna be as big of a gain as transformers were over LSTMs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sam Altman, the CEO of the company most invested in the transformer is telling a room of students it isn&amp;rsquo;t the final form. So what comes after the transformer? He&amp;rsquo;s probably right that something will, and the evidence is no longer anecdotal. Several recent papers have proved that the transformer&amp;rsquo;s worst properties are structural, not engineering problems to be fixed with better data or more compute, but mathematical lower bounds.&lt;/p&gt;
&lt;p&gt;The transformer, born from the 2017 paper &lt;a href="https://arxiv.org/abs/1706.03762"&gt;&amp;ldquo;Attention Is All You Need,&amp;rdquo;&lt;/a&gt; took us from barely-coherent GPT-2 to GPT-4 in five years. An extraordinary run. But &lt;a href="https://arxiv.org/abs/2209.04881"&gt;Duman Keles et al.&lt;/a&gt; proved that O(n²) attention complexity isn&amp;rsquo;t an implementation detail. It&amp;rsquo;s a necessary lower bound unless a foundational conjecture in complexity theory turns out to be wrong. Double the context, quadruple the cost. The KV cache for a 70B model at one-million-token context eats roughly &lt;strong&gt;320 GB&lt;/strong&gt; of GPU memory. Most hardware can&amp;rsquo;t hold it.&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-last-architecture-quadratic-attention-1-png-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/last-architecture-quadratic-attention-1.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/last-architecture-quadratic-attention-1.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/last-architecture-quadratic-attention-1.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/last-architecture-quadratic-attention-1.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-quadratic-attention-1.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/last-architecture-quadratic-attention-1.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/last-architecture-quadratic-attention-1.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/last-architecture-quadratic-attention-1.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-quadratic-attention-1.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/last-architecture-quadratic-attention-1.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/last-architecture-quadratic-attention-1.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-quadratic-attention-1.png"
alt="Quadratic attention scaling: a 4x4 attention matrix requires 16 computations while an 8x8 matrix requires 64, showing how doubling context quadruples cost in transformer architectures"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-last-architecture-quadratic-attention-1-png-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/last-architecture-quadratic-attention-1.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Quadratic attention scaling: a 4x4 attention matrix requires 16 computations while an 8x8 matrix requires 64, showing how doubling context quadruples cost in transformer architectures" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;The problems run deeper than compute costs. &lt;a href="https://arxiv.org/abs/2311.14648"&gt;Kalai and Vempala&lt;/a&gt; proved that any calibrated language model &lt;em&gt;must&lt;/em&gt; hallucinate at a certain rate. A &lt;a href="https://arxiv.org/abs/2509.04664"&gt;2025 follow-up&lt;/a&gt; goes further: no computable LLM can be universally correct on unbounded queries. Not fixable with better training data. Not fixable with RLHF. A statistical property of how these models generate text.&lt;/p&gt;
&lt;p&gt;On reasoning: &lt;a href="https://arxiv.org/abs/2305.18654"&gt;Dziri et al.&lt;/a&gt; showed transformers collapse multi-step reasoning into pattern matching. Performance drops exponentially as task complexity rises. GPT-4 gets &lt;strong&gt;59%&lt;/strong&gt; on 3-digit multiplication. &lt;a href="https://arxiv.org/abs/2603.10123"&gt;Chowdhury&lt;/a&gt; proved the &amp;ldquo;lost in the middle&amp;rdquo; problem, models performing 20-30% worse on information buried mid-context, is a geometric property of the architecture itself. Present at initialization already, before any training occurs.&lt;/p&gt;
&lt;p&gt;These are theorems. The architecture that runs every frontier AI system has a ceiling, and the ceiling is proved.&lt;/p&gt;
&lt;h2 id="the-post-transformer-stack-is-already-in-production"&gt;The post-transformer stack is already in production&lt;/h2&gt;
&lt;p&gt;A &lt;a href="https://arxiv.org/abs/2510.05364"&gt;survey by Fichtl et al.&lt;/a&gt; checked the top 10 models on every major benchmark. Zero were non-transformer. The transformer is still winning on the leaderboards. But the field is moving toward hybrid architectures. Over &lt;strong&gt;60%&lt;/strong&gt; of frontier models released in 2025 already use Mixture of Experts. &lt;a href="https://arxiv.org/abs/2412.19437"&gt;DeepSeek-V3&lt;/a&gt; has 671B total parameters but activates only 37B per token. It trained for &lt;strong&gt;2.788 million H800 GPU hours&lt;/strong&gt;, a fraction of what a comparable dense model would require, and matched frontier closed-source performance. By late 2025, &lt;a href="https://c3.unu.edu/blog/inside-deepseeks-end-of-year-ai-breakthrough-what-the-new-models-deliver"&gt;DeepSeek-V3.2 reportedly hit GPT-5-level performance at 90% lower training cost&lt;/a&gt;. MoE doesn&amp;rsquo;t replace the transformer. It changes the economics so radically that it&amp;rsquo;s arguably the single biggest practical advance since the original architecture.&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-last-architecture-moe-routing-1-png-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/last-architecture-moe-routing-1.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/last-architecture-moe-routing-1.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/last-architecture-moe-routing-1.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/last-architecture-moe-routing-1.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-moe-routing-1.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/last-architecture-moe-routing-1.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/last-architecture-moe-routing-1.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/last-architecture-moe-routing-1.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-moe-routing-1.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/last-architecture-moe-routing-1.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/last-architecture-moe-routing-1.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-moe-routing-1.png"
alt="Mixture of Experts routing: an input token passes through a router that activates only 2 of 8 expert blocks, meaning DeepSeek-V3 uses just 37B of its 671B total parameters per token"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-last-architecture-moe-routing-1-png-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/last-architecture-moe-routing-1.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Mixture of Experts routing: an input token passes through a router that activates only 2 of 8 expert blocks, meaning DeepSeek-V3 uses just 37B of its 671B total parameters per token" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;The more interesting part is what happens when you blend attention with state space models. &lt;a href="https://goombalab.github.io/blog/2024/mamba2-part1-model/"&gt;Gu and Dao (2024)&lt;/a&gt; proved SSMs and attention are mathematically dual: two views of the same computation. That theoretical result is showing up in production. &lt;a href="https://www.ai21.com/jamba/"&gt;AI21&amp;rsquo;s Jamba&lt;/a&gt; runs a 1:7 attention-to-Mamba ratio and gets &lt;strong&gt;256K&lt;/strong&gt; context at &lt;strong&gt;3x&lt;/strong&gt; throughput over Mixtral. Alibaba&amp;rsquo;s Qwen3-Next shipped the first top-tier model with a hybrid backbone: &lt;a href="https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/08_deltanet/README.md"&gt;Gated DeltaNet&lt;/a&gt; for linear attention at a 3:1 ratio with full attention. Microsoft&amp;rsquo;s Phi-4-mini-flash-reasoning is 75% Mamba layers with &lt;strong&gt;10x&lt;/strong&gt; throughput at &lt;strong&gt;2-3x&lt;/strong&gt; lower latency.&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-last-architecture-hybrid-layer-stack-1-png-3" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/last-architecture-hybrid-layer-stack-1.png"
alt="Hybrid layer stack comparison: a traditional transformer uses 8 attention layers while Jamba uses a 1:7 attention-to-Mamba ratio, achieving 256K context at 3x throughput with the same quality"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-last-architecture-hybrid-layer-stack-1-png-3" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/last-architecture-hybrid-layer-stack-1.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Hybrid layer stack comparison: a traditional transformer uses 8 attention layers while Jamba uses a 1:7 attention-to-Mamba ratio, achieving 256K context at 3x throughput with the same quality" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;Diffusion language models are the wild card. &lt;a href="https://arxiv.org/abs/2502.09992"&gt;LLaDA&lt;/a&gt;, the first 8B-parameter diffusion LLM, treats text generation as denoising rather than sequential token prediction. It matches Llama3-8B and does something no autoregressive model can: it solves the &amp;ldquo;reversal curse,&amp;rdquo; outperforming GPT-4o on reversal tasks. &lt;a href="https://medium.com/@ML-today/diffusion-models-for-language-from-early-promise-to-a-bold-new-frontier-with-llada-and-the-rise-of-ee80c7ffb8fa"&gt;Gemini Diffusion&lt;/a&gt; hit &lt;strong&gt;1,479 tokens per second&lt;/strong&gt;. Over 50 papers on diffusion LLMs appeared in 2025. If parallel generation works reliably at scale, inference economics change completely.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://arxiv.org/pdf/2510.05364"&gt;Alman and Yu&lt;/a&gt; proved there are tasks where every subquadratic alternative has a fundamental theoretical gap. That&amp;rsquo;s the strongest mathematical argument for why hybrids, not clean replacements, are what comes next.&lt;/p&gt;
&lt;h2 id="the-search-is-no-longer-human-speed"&gt;The search is no longer human-speed&lt;/h2&gt;
&lt;p&gt;The part of this I find most interesting is the recursion. AI systems are now running the search for their own architectural successors.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/"&gt;AlphaEvolve&lt;/a&gt; an evolutionary coding agent built on Gemini 2.0 found a way to multiply 4x4 complex matrices in 48 scalar multiplications: the first improvement on Strassen&amp;rsquo;s 56-year-old bound. Across &lt;a href="https://www.infoq.com/news/2025/05/google-alpha-evolve/"&gt;50+ open math problems&lt;/a&gt;, it matched the best known solutions 75% of the time and beat them 20% of the time. The recursive part: AlphaEvolve found a &lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/alphaevolve-on-google-cloud"&gt;23% speedup on a kernel inside Gemini&amp;rsquo;s own architecture&lt;/a&gt;, cutting Gemini&amp;rsquo;s training time by 1% and recovering &lt;strong&gt;0.7%&lt;/strong&gt; of Google&amp;rsquo;s total compute. Gemini making Gemini faster.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.marktechpost.com/2026/03/08/andrej-karpathy-open-sources-autoresearch-a-630-line-python-tool-letting-ai-agents-run-autonomous-ml-experiments-on-single-gpus/"&gt;Karpathy&amp;rsquo;s AutoResearch&lt;/a&gt;, released March 7, 2026, is a 630-line Python script that lets an AI agent modify training code, run 5-minute experiments, check results, and iterate. He pointed it at his own highly-tuned &amp;ldquo;Time to GPT-2&amp;rdquo; codebase. The agent found about 20 additive improvements that transferred to larger models, cutting the metric by &lt;strong&gt;11%&lt;/strong&gt;. &lt;a href="https://officechai.com/ai/andrej-karpathys-autoresearch-project-lets-agents-run-100-ai-research-experiments-while-you-sleep/"&gt;Shopify CEO Tobi Lutke tried it overnight&lt;/a&gt;: 37 experiments, 19% validation improvement, a 0.8B model outperforming a 1.6B one. &lt;a href="https://github.com/SakanaAI/AI-Scientist-v2"&gt;Sakana AI&amp;rsquo;s AI Scientist v2&lt;/a&gt; went further and produced the first AI-authored paper accepted through standard peer review. &lt;a href="https://controlai.news/p/the-ultimate-risk-recursive-self"&gt;OpenAI said publicly in late 2025&lt;/a&gt; that it&amp;rsquo;s researching how to safely build AI systems capable of recursive self-improvement. Two years ago this was a thought experiment.&lt;/p&gt;
&lt;h2 id="what-the-hardware-decides"&gt;What the hardware decides&lt;/h2&gt;
&lt;p&gt;The transformer won not because attention was theoretically prettier than recurrence. It won because it parallelized well on GPUs. Whatever comes next has to clear the same bar.&lt;/p&gt;
&lt;p&gt;Pre-training scaling for dense transformers is flattening. &lt;a href="https://fortune.com/2025/02/25/what-happened-gpt-5-openai-orion-pivot-scaling-pre-training-llm-agi-reasoning/"&gt;OpenAI spent at least $500 million per major training run on Orion&lt;/a&gt;. The model hit GPT-4 performance after 20% of training; the remaining 80% gave diminishing returns. They downgraded it from GPT-5 to GPT-4.5. &lt;a href="https://artificialintelligencemonaco.substack.com/p/ilya-sutskever-on-superintelligence"&gt;Sutskever&lt;/a&gt; at NeurIPS 2024: &amp;ldquo;Pre-training as we know it will end. The data is not growing because we have but one internet.&amp;rdquo; His startup SSI has &lt;a href="https://www.arturmarkus.com/ilya-sutskevers-ssi-raises-1b-at-30b-valuation-with-zero-revenue-6x-jump-in-5-months-redefines-ai-investment-logic/"&gt;raised to a $32 billion valuation with about 20 employees and zero revenue&lt;/a&gt;. A bet that the next leap requires something architecturally new.&lt;/p&gt;
&lt;p&gt;But test-time compute opened a different axis entirely. OpenAI&amp;rsquo;s o3 hit &lt;strong&gt;87.5%&lt;/strong&gt; on ARC-AGI, beating most humans. DeepSeek-R1 matched o1-level reasoning at &lt;strong&gt;70%&lt;/strong&gt; lower cost. &lt;a href="https://aibusiness.com/language-models/ai-model-scaling-isn-t-over-it-s-entering-a-new-era"&gt;OpenAI&amp;rsquo;s inference spending reached $2.3 billion in 2024&lt;/a&gt;: &lt;strong&gt;15x&lt;/strong&gt; what they spent training GPT-4.5. &lt;a href="https://www.dwarkesh.com/p/dario-amodei"&gt;Dario Amodei&lt;/a&gt; at Morgan Stanley in March 2026: &amp;ldquo;We do not see hitting the wall. We don&amp;rsquo;t see a wall.&amp;rdquo; He&amp;rsquo;s talking about this axis, inference-time compute and RL from verifiable rewards, not about pre-training bigger dense models. The Densing Law now shows capability per parameter doubling every &lt;strong&gt;3.5 months&lt;/strong&gt; through better data, MoE, and distillation. Last year&amp;rsquo;s frontier, matched with a fraction of the parameters.&lt;/p&gt;
&lt;p&gt;Inference demand is projected to &lt;a href="https://v-chandra.github.io/on-device-llms/"&gt;exceed training demand by 118x&lt;/a&gt;. Global data center power is heading toward &lt;a href="https://www.iea.org/reports/energy-and-ai/executive-summary"&gt;945 TWh by 2030&lt;/a&gt;, roughly Japan&amp;rsquo;s total electricity consumption. An architecture that scores 2x better on benchmarks but runs 3x worse at inference won&amp;rsquo;t win. What ships is whatever fits the hardware. The transformer isn&amp;rsquo;t going away. It&amp;rsquo;s becoming one component in a larger stack: attention for recall, SSMs for cheap sequence processing, MoE for capacity, maybe diffusion for parallel output. &lt;a href="https://www.ai21.com/jamba/"&gt;Jamba&lt;/a&gt;, &lt;a href="https://arxiv.org/html/2411.13676v1"&gt;Hymba&lt;/a&gt;, and Qwen3-Next already ship this way. That&amp;rsquo;s not a prediction. It&amp;rsquo;s what&amp;rsquo;s in production.&lt;/p&gt;
&lt;p&gt;How fast the stack evolves is the open question. The answer, given AlphaEvolve and AutoResearch and AI Scientist v2, is faster than any previous architectural transition. I don&amp;rsquo;t know whether the transformer remains the dominant layer for two years or five. But I&amp;rsquo;m fairly confident that whatever comes next, humans won&amp;rsquo;t have designed it alone.&lt;/p&gt;</description></item><item><title>MCP vs A2A in 2026: How the AI Protocol War Ends</title><link>https://philippdubach.com/posts/mcp-vs-a2a-in-2026-how-the-ai-protocol-war-ends/</link><pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/mcp-vs-a2a-in-2026-how-the-ai-protocol-war-ends/</guid><description>&lt;p&gt;On March 26, 2025, Sam Altman posted the following &lt;a href="https://x.com/sama/status/1904957253456941061"&gt;three sentences&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;people love MCP and we are excited to add support across our products.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;MCP is Anthropic&amp;rsquo;s Model Context Protocol. OpenAI is Anthropic&amp;rsquo;s most direct competitor. Altman was endorsing a rival&amp;rsquo;s standard. That post may be the most significant event in enterprise AI infrastructure this year. When your main competitor adopts your protocol, the war is close to over. I&amp;rsquo;ve been watching this play out since &lt;a href="https://www.anthropic.com/news/model-context-protocol"&gt;Anthropic launched MCP in November 2024&lt;/a&gt;, and I want to work through what&amp;rsquo;s happening: who controls what, what &amp;ldquo;interoperability&amp;rdquo; means in practice, and whether any of this follows patterns we&amp;rsquo;ve seen before.&lt;/p&gt;
&lt;h2 id="what-is-mcp"&gt;What is MCP&lt;/h2&gt;
&lt;p&gt;MCP is a client-server protocol, licensed MIT, built on JSON-RPC 2.0. The mental model is simple: an AI agent (the host) connects through a client to MCP servers that expose tools, data sources, and context. Instead of building a bespoke integration every time Claude or GPT needs to talk to Salesforce, GitHub, or your internal database, you build one MCP server. Any compatible host can then use it.&lt;/p&gt;
&lt;p&gt;The problem it solves, which explains why it spread so fast, is that without a standard like this, integration complexity grows quadratically. Every new AI model times every new tool equals a new custom integration. MCP tries to make it linear.&lt;/p&gt;
&lt;p&gt;By December 2025, &lt;a href="https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation"&gt;Anthropic&amp;rsquo;s own count&lt;/a&gt; put the public MCP server ecosystem at &lt;strong&gt;10,000+&lt;/strong&gt; active servers and &lt;strong&gt;97 million&lt;/strong&gt; monthly SDK downloads across the Python and TypeScript SDKs. &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/"&gt;GitHub&amp;rsquo;s 2025 Octoverse report&lt;/a&gt; flagged MCP as a standout, hitting &lt;strong&gt;37,000 stars&lt;/strong&gt; in eight months. The unofficial registry mcp.so lists over 18,000 servers. Official SDKs now cover ten languages, including Python, TypeScript, Java, C#, Go, Kotlin, Rust, and Swift.&lt;/p&gt;
&lt;p&gt;The companies building MCP integrations: Microsoft, Salesforce, Cloudflare, GitHub, Stripe, Atlassian, Figma, Snowflake, Databricks, New Relic. At &lt;a href="https://blog.cloudflare.com/mcp-demo-day/"&gt;Cloudflare&amp;rsquo;s MCP Demo Day in May 2025&lt;/a&gt;, Asana, PayPal, Sentry, and Webflow all shipped remote servers in a single afternoon. Gartner predicts 75% of API gateway vendors will have MCP features by 2026.&lt;/p&gt;
&lt;p&gt;OpenAI&amp;rsquo;s adoption went beyond Altman&amp;rsquo;s post. MCP support rolled out across their Agents SDK (March 2025), &lt;a href="https://openai.com/index/new-tools-and-features-in-the-responses-api/"&gt;Responses API (May 2025)&lt;/a&gt;, &lt;a href="https://openai.com/index/introducing-gpt-realtime/"&gt;Realtime API (August 2025)&lt;/a&gt;, and &lt;a href="https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt-beta"&gt;ChatGPT Developer Mode (September 2025)&lt;/a&gt;. The two companies later &lt;a href="http://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/"&gt;co-authored the MCP Apps Extension&lt;/a&gt;. You don&amp;rsquo;t see that often between direct competitors.&lt;/p&gt;
&lt;p&gt;One performance claim circulates in blog posts and marketing materials: that organizations implementing MCP report &amp;ldquo;40–60% faster agent deployment times.&amp;rdquo; I have not found a primary source for this. No survey, no case study, no named company. I&amp;rsquo;d treat it as marketing content until someone produces the underlying data.&lt;/p&gt;
&lt;h2 id="googles-a2a-fills-a-different-layer"&gt;Google&amp;rsquo;s A2A fills a different layer&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/"&gt;Google launched A2A, the Agent-to-Agent protocol, at Cloud Next on April 9, 2025&lt;/a&gt;, five months after MCP. Google didn&amp;rsquo;t position A2A as MCP replacement. They called it a complement. I think that&amp;rsquo;s honest, but it takes a minute to see why.&lt;/p&gt;
&lt;p&gt;MCP connects an agent to tools; A2A connects agents to each other, the two protocols produce different behavior.&lt;/p&gt;
&lt;p&gt;When an MCP host calls an MCP server, it knows exactly what it&amp;rsquo;s getting: structured tool descriptions, specific function signatures, predictable outputs. The agent can see inside the tool. A2A works differently. Agents remain opaque to each other. An A2A agent publishes an &amp;ldquo;Agent Card,&amp;rdquo; a JSON metadata document at a well-known URL, describing its capabilities and authentication requirements. Other agents discover it, negotiate tasks through a defined lifecycle (submitted, working, input-required, completed), and collaborate without sharing memory or internal state.&lt;/p&gt;
&lt;p&gt;Google&amp;rsquo;s own documentation uses a repair shop analogy. MCP is how the mechanic uses diagnostic equipment. A2A is how the customer talks to the shop manager, or how the manager coordinates with a parts supplier. It works: both conversations happen in a real repair shop, and cutting either one doesn&amp;rsquo;t simplify anything.&lt;/p&gt;
&lt;p&gt;A2A &lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/"&gt;launched with 50+ partner organizations&lt;/a&gt; and &lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/agent2agent-protocol-is-getting-an-upgrade"&gt;grew to 150+ by July 2025&lt;/a&gt;. The list includes Atlassian, Salesforce, SAP, ServiceNow, McKinsey, BCG, Accenture. &lt;a href="https://developers.googleblog.com/en/google-cloud-donates-a2a-to-linux-foundation/"&gt;Google donated A2A to the Linux Foundation in June 2025&lt;/a&gt;. &lt;a href="https://lfaidata.foundation/communityblog/2025/08/29/acp-joins-forces-with-a2a-under-the-linux-foundations-lf-ai-data/"&gt;IBM&amp;rsquo;s competing Agent Communication Protocol merged into A2A in August&lt;/a&gt;, with IBM&amp;rsquo;s engineers joining the technical steering committee. As of February 2026, A2A has roughly &lt;strong&gt;21,900 GitHub stars&lt;/strong&gt;, about 40% of MCP&amp;rsquo;s total. &lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-mcp-vs-a2a-protocol-race-png-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/mcp-vs-a2a-protocol-race.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/mcp-vs-a2a-protocol-race.png"
alt="Exhibit comparing MCP and A2A protocol adoption: MCP leads with 37,000 GitHub stars, 18,000&amp;#43; public servers, 97M monthly SDK downloads, and 10 SDK languages versus A2A at 21,900 stars, no public registry, and 3 languages"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-mcp-vs-a2a-protocol-race-png-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/mcp-vs-a2a-protocol-race.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit comparing MCP and A2A protocol adoption: MCP leads with 37,000 GitHub stars, 18,000&amp;#43; public servers, 97M monthly SDK downloads, and 10 SDK languages versus A2A at 21,900 stars, no public registry, and 3 languages" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;h2 id="what-history-can-tell-us-about-how-this-ends"&gt;What history can tell us about how this ends&lt;/h2&gt;
&lt;p&gt;AI agent protocol wars have a consistent pattern. The winner is almost never the technically superior option. It&amp;rsquo;s the one that ships first and gets adopted before anyone can catch up.&lt;/p&gt;
&lt;p&gt;TCP/IP and OSI are the canonical example. The OSI model, published by ISO in 1983, was architecturally more rigorous than TCP/IP&amp;rsquo;s four-layer stack. It had real institutional backing: the US Commerce Department published its GOSIP mandate in August 1988, with formal enforcement beginning in 1990. European governments followed. OSI still lost. TCP/IP won because it had running code, freely available implementations bundled with BSD Unix workstations, while OSI remained elegant theory trapped in committee processes. By 1994 the outcome was obvious. David Clark&amp;rsquo;s &lt;a href="https://groups.csail.mit.edu/ana/People/DDC/future_ietf_92.pdf"&gt;IETF motto captures why&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We reject kings, presidents and voting. We believe in rough consensus and running code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;VHS versus Betamax is the other lesson people cite, often incorrectly. Betamax had better picture quality. VHS won anyway, and the usual explanation is the movie library. That&amp;rsquo;s part of it. But JVC openly licensed VHS to manufacturers across the industry, which drove prices down and built a content ecosystem Sony couldn&amp;rsquo;t match. By 1987, &lt;a href="https://en.wikipedia.org/wiki/Videotape_format_war"&gt;VHS held 90% of the US VCR market&lt;/a&gt;. Sony conceded in 1988 by manufacturing VHS players. Ecosystem breadth, once established, creates a gravitational field that technical superiority alone can&amp;rsquo;t escape.&lt;/p&gt;
&lt;p&gt;USB is a more recent example with a twist. The consortium, Compaq, DEC, IBM, Intel, Microsoft, NEC, Nortel, formed in 1994 and &lt;a href="https://ethw.org/Milestones:Universal_Serial_Bus_(USB),_1996"&gt;shipped USB 1.0 in January 1996&lt;/a&gt;. Adoption was sluggish until &lt;a href="https://en.wikipedia.org/wiki/IMac_G3"&gt;Apple shipped the iMac G3 in August 1998&lt;/a&gt; with only USB ports, forcing the entire peripheral industry to follow. One player is so central to the ecosystem that their adoption forces everyone else&amp;rsquo;s hand. OpenAI adopting MCP in March 2025 is MCP&amp;rsquo;s iMac moment.&lt;/p&gt;
&lt;p&gt;But USB also offers a warning. USB-C&amp;rsquo;s physical connector won universally, then the underlying protocol fragmented. The same connector could carry anything from USB 2.0 to USB4, 5W to 240W of power, depending on what you plugged together. &lt;a href="https://single-market-economy.ec.europa.eu/sectors/electrical-and-electronic-engineering-industries-eei/radio-equipment-directive-red/one-common-charging-solution-all_en"&gt;The EU eventually legislated convergence through its Radio Equipment Directive, which took effect December 28, 2024&lt;/a&gt;. A standard can win and still fragment when nobody governs the details. &lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-standards-war-precedents-png-3" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/standards-war-precedents.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/standards-war-precedents.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/standards-war-precedents.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/standards-war-precedents.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/standards-war-precedents.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/standards-war-precedents.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/standards-war-precedents.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/standards-war-precedents.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/standards-war-precedents.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/standards-war-precedents.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/standards-war-precedents.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/standards-war-precedents.png"
alt="Exhibit comparing historical standards wars: TCP/IP versus OSI decided by running code, VHS versus Betamax decided by open licensing, USB decided by Apple iMac catalyst event, all paralleling MCP ecosystem-first trajectory"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-standards-war-precedents-png-3" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/standards-war-precedents.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit comparing historical standards wars: TCP/IP versus OSI decided by running code, VHS versus Betamax decided by open licensing, USB decided by Apple iMac catalyst event, all paralleling MCP ecosystem-first trajectory" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;h2 id="what-now"&gt;What now?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation"&gt;The Linux Foundation&amp;rsquo;s Agentic AI Foundation (AAIF), launched December 9, 2025&lt;/a&gt; with Anthropic, OpenAI, and Block as co-founders, &lt;a href="https://www.linuxfoundation.org/press/agentic-ai-foundation-welcomes-97-new-members"&gt;now has 146 member organizations&lt;/a&gt;, including JPMorgan Chase, American Express, Autodesk, Red Hat, and Huawei. A2A has its own Linux Foundation governance body. MCP sits within AAIF. Both are under the same umbrella, but they&amp;rsquo;re not the same project.&lt;/p&gt;
&lt;p&gt;This is the governance structure you typically see after a standards war has been decided in principle but before the implementation details have been hammered out. Think of the W3C in 1994, not the W3C in 1998. For anyone making architectural decisions right now, the practical question isn&amp;rsquo;t MCP versus A2A. Most major enterprise platforms already support both. Salesforce, SAP, IBM, Microsoft, and AWS have committed to both. The question is sequencing and depth.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://research.isg-one.com/analyst-perspectives/a2a-v-mcp-why-ai-agents-need-both"&gt;ISG analyst David Menninger&lt;/a&gt; put it clearly: &amp;ldquo;MCP first for sharing context; then A2A for dynamic interaction among agents.&amp;rdquo; That&amp;rsquo;s the sequence I&amp;rsquo;d follow. MCP is the more mature protocol with the larger server ecosystem. The 10,000+ existing servers represent integration work that doesn&amp;rsquo;t need to be rebuilt. Start there. Layer A2A on top when your use cases require multi-agent coordination across organizational boundaries, supply chain, cross-platform orchestration, which is exactly where the Tyson Foods and Adobe deployments have landed.&lt;/p&gt;
&lt;p&gt;MCP security deserves a separate conversation. &lt;a href="https://astrix.security/learn/blog/state-of-mcp-server-security-2025/"&gt;Astrix Security&amp;rsquo;s research&lt;/a&gt; found that 53% of MCP servers rely on static credentials rather than OAuth. A critical vulnerability in the mcp-remote npm package (CVE-2025-6514) exposed 437,000+ installations to shell injection. TCP/IP had its share of early-stage security problems in the 1980s, so I&amp;rsquo;m not calling this fatal. But these are real vulnerabilities, and they will cause real incidents before the posture matures.&lt;/p&gt;
&lt;p&gt;Multiple analyst firms converge on an agentic AI market of roughly &lt;strong&gt;$7–8 billion in 2025&lt;/strong&gt;, growing at 40–50% annually, with projections ranging from &lt;a href="https://www.grandviewresearch.com/industry-analysis/ai-agents-market-report"&gt;$50 billion by 2030&lt;/a&gt; to &lt;a href="https://www.precedenceresearch.com/agentic-ai-market"&gt;$199 billion by 2034&lt;/a&gt;. NVIDIA&amp;rsquo;s CUDA is the comparison that matters: 4 million developers, 15 years of compounding library investment, and switching costs that produce &lt;a href="https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025"&gt;$130.5 billion in annual revenue at 73% gross margins&lt;/a&gt;. MCP&amp;rsquo;s 97 million monthly downloads aren&amp;rsquo;t CUDA yet. But the trajectory points the same direction. &lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-agentic-ai-market-trajectory-png-5" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/agentic-ai-market-trajectory.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/agentic-ai-market-trajectory.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/agentic-ai-market-trajectory.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/agentic-ai-market-trajectory.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/agentic-ai-market-trajectory.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/agentic-ai-market-trajectory.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/agentic-ai-market-trajectory.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/agentic-ai-market-trajectory.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/agentic-ai-market-trajectory.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/agentic-ai-market-trajectory.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/agentic-ai-market-trajectory.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/agentic-ai-market-trajectory.png"
alt="Exhibit showing agentic AI market projections from $7-8 billion in 2025 to $50 billion by 2030 and up to $199 billion by 2034, with consensus 45% CAGR and comparison to NVIDIA CUDA $131B annual revenue"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-agentic-ai-market-trajectory-png-5" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/agentic-ai-market-trajectory.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit showing agentic AI market projections from $7-8 billion in 2025 to $50 billion by 2030 and up to $199 billion by 2034, with consensus 45% CAGR and comparison to NVIDIA CUDA $131B annual revenue" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;p&gt;My best guess (and I want to be clear it&amp;rsquo;s a guess): MCP becomes the infrastructure layer, A2A becomes the coordination layer, much as TCP handles transport while HTTP handles application-layer communication. Different floors of the same building. The question remains whether 146 AAIF members can hold coherent standards against the competitive pressure of &lt;a href="https://tracxn.com/d/sectors/agentic-ai/__oyRAfdUfHPjf2oap110Wis0Qg12Gd8DzULlDXPJzrzs"&gt;over 1,000 active agentic AI startups&lt;/a&gt;, each with economic incentives to differentiate.&lt;/p&gt;</description></item><item><title>93% of Developers Use AI Coding Tools. Productivity Hasn't Moved.</title><link>https://philippdubach.com/posts/93-of-developers-use-ai-coding-tools.-productivity-hasnt-moved./</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/93-of-developers-use-ai-coding-tools.-productivity-hasnt-moved./</guid><description>&lt;p&gt;A &lt;a href="https://arxiv.org/abs/2507.09089"&gt;study published in July 2025&lt;/a&gt; gave AI coding tools their most credible test yet. Sixteen experienced open-source developers, 246 real tasks, randomized controlled design. The researchers expected to measure how much faster AI made them. What they found: developers using AI took &lt;strong&gt;19% longer&lt;/strong&gt; to complete tasks than those working without it.&lt;/p&gt;
&lt;p&gt;The developers themselves thought they were 20% faster.&lt;/p&gt;
&lt;p&gt;That &lt;strong&gt;39-point gap&lt;/strong&gt; between perception and reality is the most important number in &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/"&gt;METR&amp;rsquo;s paper&lt;/a&gt;. It lands inside two years of adoption data pointing in the opposite direction. &lt;a href="https://getdx.com/"&gt;DX&lt;/a&gt; surveyed 121,000 developers across 450+ companies and found &lt;strong&gt;92.6%&lt;/strong&gt; use AI coding tools at least monthly. &lt;a href="https://blog.jetbrains.com/ai/2026/02/the-best-ai-models-for-coding-accuracy-integration-and-developer-fit/"&gt;JetBrains&amp;rsquo; AI Pulse&lt;/a&gt; measured 93%. The &lt;a href="https://dora.dev/dora-report-2025"&gt;DORA 2025 report&lt;/a&gt; put it at 90%. On the productivity side: six independent research efforts converge on roughly the same ceiling, &lt;strong&gt;10%&lt;/strong&gt; at the system level, if you&amp;rsquo;re being generous.&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-ai-coding-perception-gap-png-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/ai-coding-perception-gap.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/ai-coding-perception-gap.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/ai-coding-perception-gap.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/ai-coding-perception-gap.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-perception-gap.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/ai-coding-perception-gap.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/ai-coding-perception-gap.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/ai-coding-perception-gap.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-perception-gap.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/ai-coding-perception-gap.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/ai-coding-perception-gap.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-perception-gap.png"
alt="Exhibit showing METR study results: developers using AI took 19% longer to complete tasks while believing they were 20% faster, a 39-point perception gap across 246 tasks with 56% of AI suggestions rejected"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-ai-coding-perception-gap-png-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/ai-coding-perception-gap.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit showing METR study results: developers using AI took 19% longer to complete tasks while believing they were 20% faster, a 39-point perception gap across 246 tasks with 56% of AI suggestions rejected" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;h2 id="the-bottleneck-was-never-the-typing"&gt;The bottleneck was never the typing&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.tocinstitute.org/theory-of-constraints.html"&gt;Goldratt&amp;rsquo;s Theory of Constraints&lt;/a&gt; makes the following prediction: optimizing a step that isn&amp;rsquo;t the bottleneck doesn&amp;rsquo;t improve system throughput. You can make the fastest machine on the factory floor twice as fast. If it&amp;rsquo;s feeding a queue that&amp;rsquo;s already backed up, you&amp;rsquo;ve accomplished nothing at the output level.&lt;/p&gt;
&lt;p&gt;Writing code has never been that bottleneck. &lt;a href="https://www.bain.com/insights/from-pilots-to-payoff-generative-ai-in-software-development-technology-report-2025/"&gt;Bain&amp;rsquo;s analysis&lt;/a&gt; found that writing and testing code accounts for roughly 25-35% of the total software development lifecycle. The rest goes to code review, understanding requirements, debugging, meetings, documentation. Even with a 100% speedup on the coding step, that gives you a 15-25% overall improvement, and that&amp;rsquo;s before accounting for what happens downstream when you generate a lot more code. Gergely Orosz, who runs The Pragmatic Engineer, &lt;a href="https://aws.amazon.com/blogs/enterprise-strategy/measuring-the-impact-of-ai-assistants-on-software-development/"&gt;put it directly&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Speed of typing out code has never been the bottleneck for software development.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What the data shows now is that AI tools don&amp;rsquo;t just fail to clear the bottleneck. They move it downstream and make it worse. &lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-ai-coding-impact-ceiling-png-1" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/ai-coding-impact-ceiling.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/ai-coding-impact-ceiling.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/ai-coding-impact-ceiling.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/ai-coding-impact-ceiling.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-impact-ceiling.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/ai-coding-impact-ceiling.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/ai-coding-impact-ceiling.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/ai-coding-impact-ceiling.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-impact-ceiling.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/ai-coding-impact-ceiling.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/ai-coding-impact-ceiling.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-impact-ceiling.png"
alt="Exhibit showing coding is 25-35% of the software development lifecycle with developers writing code only 52 minutes per day, meaning even a 100% coding speedup yields at most 15% system improvement under Amdahl&amp;#39;s Law"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-ai-coding-impact-ceiling-png-1" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/ai-coding-impact-ceiling.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit showing coding is 25-35% of the software development lifecycle with developers writing code only 52 minutes per day, meaning even a 100% coding speedup yields at most 15% system improvement under Amdahl&amp;#39;s Law" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;h2 id="the-code-review-bottleneck"&gt;The code review bottleneck&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.faros.ai/ai-productivity-paradox"&gt;Faros AI&lt;/a&gt; measured this across 10,000+ developers on 1,255 teams in June 2025. Teams with high AI adoption completed 21% more tasks and merged 98% more pull requests. PR size grew 154%. Then: review time up 91%, bugs up 9%, organizational DORA metrics flat.&lt;/p&gt;
&lt;p&gt;More PRs, bigger PRs, slower reviews, more bugs, no throughput improvement. The coding step accelerated. The review step, already a constraint, got worse. Michael Truell, &lt;a href="https://fortune.com/2025/12/19/cursor-ai-coding-startup-graphite-competition-heats-up/"&gt;Cursor&amp;rsquo;s CEO&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cursor has made it much faster to write production code. However, for most engineering teams, reviewing code looks the same as it did three years ago&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Cursor then &lt;a href="https://cursor.com/blog/graphite"&gt;acquired Graphite&lt;/a&gt;, a code review startup. The acquisition is a more honest statement about where the constraint lives than anything in Cursor&amp;rsquo;s marketing. The &lt;a href="https://dora.dev/research/2024/dora-report/"&gt;DORA 2024 report&lt;/a&gt; found that for every 25 percentage point increase in AI adoption, delivery throughput dropped 1.5% and delivery stability dropped 7.2%. &lt;a href="https://dora.dev/dora-report-2025"&gt;DORA 2025&lt;/a&gt;, at 90% adoption, put it tersely: &amp;ldquo;AI doesn&amp;rsquo;t fix a team; it amplifies what&amp;rsquo;s already there.&amp;rdquo; The negative relationship with stability holds even as adoption saturates. &lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-ai-coding-bottleneck-shift-png-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/ai-coding-bottleneck-shift.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/ai-coding-bottleneck-shift.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/ai-coding-bottleneck-shift.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/ai-coding-bottleneck-shift.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-bottleneck-shift.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/ai-coding-bottleneck-shift.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/ai-coding-bottleneck-shift.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/ai-coding-bottleneck-shift.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-bottleneck-shift.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/ai-coding-bottleneck-shift.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/ai-coding-bottleneck-shift.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/ai-coding-bottleneck-shift.png"
alt="Exhibit showing Faros AI data across 10,000&amp;#43; developers: high AI adoption teams merged 98% more pull requests but review time increased 91%, bugs rose 9%, and DORA delivery metrics were unchanged"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-ai-coding-bottleneck-shift-png-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/ai-coding-bottleneck-shift.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Exhibit showing Faros AI data across 10,000&amp;#43; developers: high AI adoption teams merged 98% more pull requests but review time increased 91%, bugs rose 9%, and DORA delivery metrics were unchanged" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/p&gt;
&lt;h2 id="41-of-what"&gt;41% of what?&lt;/h2&gt;
&lt;p&gt;One number circulates constantly in press coverage: 41% of code is now AI-generated. It comes from Emad Mostaque, who took GitHub&amp;rsquo;s figure about the share of code accepted by Copilot users and &lt;a href="https://decrypt.co/147191/no-human-programmers-five-years-ai-stability-ceo"&gt;extrapolated it&lt;/a&gt; into a claim about all code everywhere. The original figure applied only to developers already using Copilot, a fraction of GitHub&amp;rsquo;s user base at the time. The extrapolation doesn&amp;rsquo;t hold.&lt;/p&gt;
&lt;p&gt;The more defensible numbers: &lt;a href="https://shiftmag.dev/this-cto-says-93-of-developers-use-ai-but-productivity-is-still-10-8013/"&gt;DX&amp;rsquo;s measurement across 4.2 million developers&lt;/a&gt; puts AI-generated production code at 26.9%. A &lt;a href="https://arxiv.org/abs/2506.08945"&gt;study published in Science&lt;/a&gt; found roughly 30% of Python functions from U.S. contributors on GitHub were AI-generated by late 2024. &lt;a href="https://fortune.com/2024/10/30/googles-code-ai-sundar-pichai/"&gt;Sundar Pichai&lt;/a&gt; said more than a quarter of all new code at Google is AI-generated. These numbers cluster around 25-30%.&lt;/p&gt;
&lt;p&gt;The inflated figure matters because it supports a specific argument: that AI has already crossed some threshold, that the transformation is done, that the productivity gains are already baked in. At 27%, AI is a meaningful contributor to software production. At 41%, you&amp;rsquo;re telling a different story, and the decisions that follow from it are different decisions.&lt;/p&gt;
&lt;p&gt;The quality picture at 27% is not reassuring. &lt;a href="https://www.businesswire.com/news/home/20250730694951/en/AI-Generated-Code-Poses-Major-Security-Risks-in-Nearly-Half-of-All-Development-Tasks-Veracode-Research-Reveals"&gt;Veracode tested 100+ LLMs&lt;/a&gt; across 80 coding tasks and found 45% of AI-generated code introduced OWASP Top 10 vulnerabilities. &lt;a href="https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation"&gt;CodeRabbit&amp;rsquo;s analysis&lt;/a&gt; found AI-generated code contains 2.74x more security vulnerabilities than human-written code. &lt;a href="https://www.blackduck.com/blog/open-source-trends-ossra-report.html"&gt;Black Duck&amp;rsquo;s 2026 OSSRA report&lt;/a&gt; found vulnerabilities per codebase up 107% year over year, the mean codebase going from 280 to 581 known vulnerabilities. &lt;a href="https://thenewstack.io/martin-fowler-on-preparing-for-ais-nondeterministic-computing/"&gt;Martin Fowler&amp;rsquo;s framing&lt;/a&gt; is still the most honest I&amp;rsquo;ve seen: &amp;ldquo;Treat every slice as a PR from a rather dodgy collaborator who&amp;rsquo;s very productive in the lines-of-code sense, but you can&amp;rsquo;t trust a thing they&amp;rsquo;re doing.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="perception-is-reality"&gt;Perception is reality&lt;/h2&gt;
&lt;p&gt;The 19% slowdown number has been contested, fairly: the CI is wide (+2% to +39%), the study covered experienced developers on complex codebases, and METR has acknowledged design limitations. In February 2026, &lt;a href="https://metr.org/blog/2026-02-24-uplift-update/"&gt;METR published an update&lt;/a&gt; changing their experiment design after discovering that 30-50% of invited developers declined to participate without AI access, a selection effect that biased the original sample toward developers who benefit least from AI. Their newer cohort (800+ tasks, 57 developers) showed a -4% slowdown with a CI of -15% to +9%, substantially less negative. METR&amp;rsquo;s conclusion: &amp;ldquo;AI likely provides productivity benefits in early 2026.&amp;rdquo; The perception gap and the bottleneck problem remain real, but the exact magnitude of the July 2025 finding should be read with that caveat.&lt;/p&gt;
&lt;p&gt;METR&amp;rsquo;s companion &lt;a href="https://arxiv.org/abs/2503.14499"&gt;Horizon benchmark&lt;/a&gt; (Kwa et al., 2025) puts numbers to that curve: the 50%-task-completion time horizon for Claude 3.7 Sonnet was 60 minutes. Claude Opus 4.6, released February 2026, reached 719 minutes. The doubling time from 2023 is approximately 128 days. METR frames the productivity result as a point on that trend, not a fixed constant, though they also note that their benchmark tasks are cleaner than real production work and performance on &amp;ldquo;messier&amp;rdquo; tasks may improve more slowly. But the perception gap itself is more robust than the exact slowdown figure, and it replicates.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://survey.stackoverflow.co/2025/ai/"&gt;Stack Overflow&amp;rsquo;s 2025 Developer Survey&lt;/a&gt; found favorable views of AI tools dropped from 70% to 60%, with 46% not trusting AI output and 66% citing &amp;ldquo;almost right but not quite&amp;rdquo; as their top frustration. &lt;a href="https://www.software.com/reports/code-time-report"&gt;Software.com&amp;rsquo;s monitoring&lt;/a&gt; of 250,000 developers found the median developer codes for 52 minutes per day, about 11% of a 40-hour week. The tools are fighting over 11% of the workday.&lt;/p&gt;
&lt;p&gt;A &lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4945566"&gt;field experiment across 4,867 developers&lt;/a&gt; from MIT, Princeton, Wharton, and Microsoft found that above-median-tenure developers showed no significant productivity increase from AI tools. The people capable of using AI most effectively are also the people most likely to catch when it&amp;rsquo;s wrong and fix it. It&amp;rsquo;s why the tools work better for junior developers on simple tasks than for senior developers on the things that actually matter most.&lt;/p&gt;
&lt;h2 id="githubs-2022-copilot-study"&gt;GitHub&amp;rsquo;s 2022 Copilot study&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://arxiv.org/abs/2302.06590"&gt;GitHub&amp;rsquo;s 2022 Copilot study&lt;/a&gt;, the &amp;ldquo;55% faster&amp;rdquo; figure, still appears in enterprise sales decks in 2026. One JavaScript task: implementing a web server with HTTP endpoints. Thirty-five completers. No assessment of output quality, test coverage, or whether the code would survive production. Confidence interval: 21% to 89%. Participants knew they were being timed for productivity.&lt;/p&gt;
&lt;p&gt;What the study actually shows is that when you pick a task specifically suited to AI assistance and measure completion time without checking correctness, AI looks fast. That&amp;rsquo;s a real finding. It&amp;rsquo;s just not the one being used to justify eight-figure licensing deals.&lt;/p&gt;
&lt;h2 id="macro-data"&gt;Macro data&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.apolloacademy.com/waiting-for-the-ai-j-curve/"&gt;Apollo&amp;rsquo;s Torsten Slok&lt;/a&gt; wrote in early 2026: &amp;ldquo;AI is everywhere except in the incoming macroeconomic data.&amp;rdquo; An &lt;a href="https://www.nber.org/papers/w34836"&gt;NBER paper from February 2026&lt;/a&gt; surveying nearly 6,000 executives found over 80% of firms reported AI had no impact on productivity over the preceding three years. Expected improvement over the next three: 1.4%.&lt;/p&gt;
&lt;p&gt;Daron Acemoglu, who shared the 2024 Nobel Prize in Economics partly for his work on technology and labor markets, &lt;a href="https://www.nber.org/papers/w32487"&gt;projected&lt;/a&gt; a 0.5% total factor productivity increase from AI over the next decade. His reasoning: the economic value of AI concentrates in a narrow set of tasks that don&amp;rsquo;t represent enough of total economic activity to move aggregate numbers. The Bain arithmetic, at macroeconomic scale.&lt;/p&gt;
&lt;p&gt;The standard optimist response is the IT comparison: computers entered enterprises in the 1970s and 1980s without producing measurable productivity improvements for a decade, then the gains came in the mid-1990s. It&amp;rsquo;s a reasonable historical parallel. I&amp;rsquo;m genuinely uncertain whether it applies. Computers replaced manual processes wholesale. AI coding tools are a faster ingredient inside a process whose other ingredients haven&amp;rsquo;t changed: the requirements still need to be understood, the review still needs to happen, the tests still need to pass. The productivity lag might resolve. Or the structure of the workflow might mean it doesn&amp;rsquo;t, even eventually. I don&amp;rsquo;t know, and the honest answer is that nobody does yet.&lt;/p&gt;
&lt;h2 id="where-the-value-actually-lands"&gt;Where the value actually lands&lt;/h2&gt;
&lt;p&gt;Exploration is faster. When I&amp;rsquo;m working on something unfamiliar, a library I haven&amp;rsquo;t used, an API I&amp;rsquo;m integrating for the first time, the startup cost drops. A working first draft arrives in minutes rather than hours. That&amp;rsquo;s real, and I notice it. Whether it shows up in throughput metrics is a different question, and the data suggests mostly not, because the constraint was never the first draft.&lt;/p&gt;
&lt;p&gt;Boilerplate, test scaffolding, documentation: these genuinely benefit too. The tasks that are well-scoped and low-stakes if approximately wrong are where these tools earn their keep. Anyone who&amp;rsquo;s used them seriously already knew this before the research said so.&lt;/p&gt;
&lt;p&gt;Simon Willison, in an &lt;a href="https://www.npr.org/2025/10/21/nx-s1-5506141/ai-code-software-productivity-claims"&gt;NPR interview&lt;/a&gt;: &amp;ldquo;Our job is not to type code into a computer. Our job is to deliver systems that solve problems.&amp;rdquo; The tools handle the first part better than they did a year ago. The second part hasn&amp;rsquo;t changed.&lt;/p&gt;
&lt;h2 id="the-right-question"&gt;The right question&lt;/h2&gt;
&lt;p&gt;The useful product question, if the bottleneck is now review, is what makes review faster and more reliable, not what generates more code faster. AI tools that flag security issues, catch logic errors, and surface context about why code was written a certain way would attack the actual constraint. This is at least part of what Cursor is working toward with Graphite.&lt;/p&gt;
&lt;p&gt;The harder problem is cultural. &lt;a href="https://www.bain.com/insights/from-pilots-to-payoff-generative-ai-in-software-development-technology-report-2025/"&gt;Bain&lt;/a&gt; and DORA say the same thing from different angles: AI amplifies what&amp;rsquo;s already there. Teams with good review practices and clear requirements get leverage. Teams without them produce more code that still doesn&amp;rsquo;t ship on time. The organizations that most want a tool to fix their velocity tend to be the ones with the process debt that prevents any tool from working.&lt;/p&gt;
&lt;p&gt;I have no idea what the five-year picture looks like. The Solow paradox took a decade to resolve and resolved in ways nobody expected. Maybe the AI productivity gains show up in 2029 and the 2026 skeptics look naive. Genuinely possible. I try to hold that view honestly rather than dismiss it.&lt;/p&gt;
&lt;p&gt;What the data shows now: at 92.6% monthly adoption and roughly 27% of production code AI-generated, the experiment has run at real scale. Organizational throughput hasn&amp;rsquo;t moved past 10%. Experienced developers are slower with AI assistance than without it. Bugs are up, review times are up, code quality metrics are declining, and DORA stability goes the wrong way as adoption increases.&lt;/p&gt;</description></item><item><title>Building a No-Tracking Newsletter from Markdown to Distribution</title><link>https://philippdubach.com/posts/building-a-no-tracking-newsletter-from-markdown-to-distribution/</link><pubDate>Wed, 24 Dec 2025 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/building-a-no-tracking-newsletter-from-markdown-to-distribution/</guid><description>&lt;p&gt;&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-Newsletter_Overview2-jpg-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/Newsletter_Overview2.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/Newsletter_Overview2.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/Newsletter_Overview2.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/Newsletter_Overview2.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview2.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/Newsletter_Overview2.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/Newsletter_Overview2.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/Newsletter_Overview2.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview2.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/Newsletter_Overview2.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/Newsletter_Overview2.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview2.jpg"
alt="Screenshot of rendered newsletter showing article preview cards with images and descriptions"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-Newsletter_Overview2-jpg-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/Newsletter_Overview2.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Screenshot of rendered newsletter showing article preview cards with images and descriptions" decoding="async"&gt;
&lt;/dialog&gt;
Friends have been asking how they can stay up to date with what I&amp;rsquo;m working on and keep track of the things I read, write, and share. RSS feeds don&amp;rsquo;t seem to be en vogue anymore, apparently. So I built a mailing list. What else would you do over the Christmas break?&lt;/p&gt;
&lt;p&gt;From a previous marketing job I knew Mailchimp. Also, every newsletter I unsubscribe from is Mailchimp. I no longer wish to receive these emails.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-unsubscribe2-png-1" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/unsubscribe2.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/unsubscribe2.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/unsubscribe2.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/unsubscribe2.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/unsubscribe2.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/unsubscribe2.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/unsubscribe2.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/unsubscribe2.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/unsubscribe2.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/unsubscribe2.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/unsubscribe2.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/unsubscribe2.png"
alt="Unsubscribe confirmation from Mailchimp newsletters"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-unsubscribe2-png-1" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/unsubscribe2.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Unsubscribe confirmation from Mailchimp newsletters" decoding="async"&gt;
&lt;/dialog&gt;
Or obviously Substack. I read &lt;a href="https://simonw.substack.com"&gt;Simon Willison&amp;rsquo;s Newsletter&lt;/a&gt; sometimes. And obviously &lt;a href="https://philippdubach.com/posts/michael-burrys-379-newsletter/"&gt;Michael Burry&amp;rsquo;s $379 Substack&lt;/a&gt;. Those are solid options, but I had a clear picture in mind of what I wanted. I wanted only HTML, no tracking (also why I use &lt;a href="https://www.goatcounter.com/"&gt;GoatCounter&lt;/a&gt; on my site and not Google Analytics), and full control of the creation and distribution chain from end to end. So I sat down and drew into my notebook, what I always do when I have an idea after a long walk or a hot shower.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-newsletter_scetch3-jpg-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/newsletter_scetch3.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/newsletter_scetch3.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/newsletter_scetch3.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/newsletter_scetch3.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_scetch3.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/newsletter_scetch3.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/newsletter_scetch3.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/newsletter_scetch3.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_scetch3.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/newsletter_scetch3.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/newsletter_scetch3.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_scetch3.jpg"
alt="Hand-drawn notebook sketch of newsletter architecture showing markdown to HTML to distribution flow"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-newsletter_scetch3-jpg-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/newsletter_scetch3.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Hand-drawn notebook sketch of newsletter architecture showing markdown to HTML to distribution flow" decoding="async"&gt;
&lt;/dialog&gt;
I then went over to Illustrator (actually &lt;a href="https://affinity.serif.com/en-us/designer/"&gt;Affinity Designer&lt;/a&gt;, which I have been happily using since my Creative Cloud subscription ran out, sorry Adobe) and built a quick mockup of my drawing. I fed the mockup to Claude to generate pure HTML. After a few iterations it more or less looked like I wanted it to be.&lt;/p&gt;
&lt;p&gt;The architecture: write the newsletter in Markdown (as I do for all of &lt;a href="https://philippdubach.com/about"&gt;my blog&lt;/a&gt;). Render it as HTML. Fetch OpenGraph images from my Cloudflare CDN at the lowest feasible resolution and pull descriptions automatically. Format links with preview cards. Keep some space for freetext at the top and bottom.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-newsletter_architecture2-png-3" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/newsletter_architecture2.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/newsletter_architecture2.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/newsletter_architecture2.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/newsletter_architecture2.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_architecture2.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/newsletter_architecture2.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/newsletter_architecture2.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/newsletter_architecture2.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_architecture2.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/newsletter_architecture2.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/newsletter_architecture2.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/newsletter_architecture2.png"
alt="Flowchart showing newsletter pipeline: Write Markdown, Render HTML, Host on R2, Fetch KV for subscribers, Send via Resend API"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-newsletter_architecture2-png-3" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/newsletter_architecture2.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Flowchart showing newsletter pipeline: Write Markdown, Render HTML, Host on R2, Fetch KV for subscribers, Send via Resend API" decoding="async"&gt;
&lt;/dialog&gt;
I built a &lt;a href="https://github.com/philippdubach/newsletter-generator"&gt;Python engine&lt;/a&gt; that renders my &lt;code&gt;.md&lt;/code&gt; files to email-safe HTML. The script handles several things automatically: (1) It fetches OpenGraph metadata for every link using Beautiful Soup, caching results to avoid repeated requests. (2) optimizes images using Cloudflare&amp;rsquo;s image transformation service. For email, I use 240px width (2x the display size of 120px for retina displays). (3) It generates LinkedIn-style preview cards with images on the left and text on the right. The output is table-based HTML because email clients from 2003 still exist and they&amp;rsquo;re apparently immortal.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-Newsletter_Overview-png-4" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/Newsletter_Overview.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/Newsletter_Overview.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/Newsletter_Overview.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/Newsletter_Overview.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/Newsletter_Overview.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/Newsletter_Overview.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/Newsletter_Overview.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/Newsletter_Overview.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/Newsletter_Overview.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/Newsletter_Overview.png"
alt="Screenshot of rendered newsletter showing article preview cards with images and descriptions"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-Newsletter_Overview-png-4" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/Newsletter_Overview.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Screenshot of rendered newsletter showing article preview cards with images and descriptions" decoding="async"&gt;
&lt;/dialog&gt;
Originally I intended to manually copy-paste the HTML into an email and send it out since I did not expect many subscribers at first (or at all). But I had another challenge at hand: how do people sign up?&lt;/p&gt;
&lt;p&gt;Since I had already been using &lt;a href="https://developers.cloudflare.com/kv/"&gt;Cloudflare Workers KV&lt;/a&gt; to build an API with historic values of my temperature and humidity sensor at home, I resorted to that. The API is simple. POST to &lt;code&gt;/api/subscribe&lt;/code&gt; with an email address, and it gets stored in KV with a timestamp and some metadata.&lt;/p&gt;
&lt;p&gt;After some Copilot iterations (I&amp;rsquo;m not a security guy, so not sure how I feel about handing all the security and testing to an agent, please reach out if you can help) the Worker includes rate limiting, honeypot fields for spam protection, proper CORS headers, and RFC-compliant email validation.&lt;/p&gt;
&lt;p&gt;I then wanted to get a confirmation email every time someone signed up. Since SMTP sending over my domain did not work reliably at first, I had to look for other options. Even though I wanted everything self-hosted, I ended up using the &lt;a href="https://resend.com/"&gt;Resend API&lt;/a&gt;. The API is straightforward:&lt;/p&gt;
&lt;div class="code-block" data-lang="typescript"&gt;&lt;span class="code-lang" aria-hidden="true"&gt;typescript&lt;/span&gt;&lt;button type="button" class="code-copy" aria-label="Copy code to clipboard"&gt;
&lt;span class="code-copy-text"&gt;Copy&lt;/span&gt;
&lt;/button&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sendWelcomeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscriberEmail&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;: &lt;span class="kt"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://api.resend.com/emails&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`Bearer &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;: &lt;span class="kt"&gt;JSON.stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Philipp Dubach &amp;lt;noreply@notifications.philippdubach.com&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subscriberEmail&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Welcome to the Newsletter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`&amp;lt;p&amp;gt;Thanks for subscribing!&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;p&gt;After implementing this, I figured: why not send a confirmation to the subscriber and a copy to me? Why not use Resend for the whole distribution? (This is not a paid advertisement.) The HTML newsletter I generate goes straight into the email body. No images hosted elsewhere (except for the optimized preview thumbnails). No tracking pixels. No click tracking. The email is just HTML.&lt;/p&gt;
&lt;p&gt;I also looked at &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt; and &lt;a href="https://sendgrid.com/"&gt;SendGrid&lt;/a&gt; before settling on Resend. Mailgun has better deliverability monitoring but a more complex API. SendGrid has more features but felt overengineered for what I needed. Resend&amp;rsquo;s free tier and simple API won. If you have strong opinions on email APIs, I&amp;rsquo;m curious to hear them.&lt;/p&gt;
&lt;p&gt;The total cost of running this: zero. Cloudflare Workers has a generous free tier. Cloudflare R2 (where the HTML newsletters are hosted) has 10GB free storage. Resend gives 3,000 emails per month. The Python script runs locally or on my Azure instance.&lt;/p&gt;
&lt;p&gt;You can find &lt;a href="https://static.philippdubach.com/newsletter/newsletter-2025-12.html"&gt;my first newsletter here&lt;/a&gt;. The full code for both the &lt;a href="https://github.com/philippdubach/newsletter-generator"&gt;newsletter generator&lt;/a&gt; and the &lt;a href="https://github.com/philippdubach/newsletter-api"&gt;subscriber API&lt;/a&gt; is on GitHub.&lt;/p&gt;</description></item><item><title>Visualizing Gradients with PyTorch</title><link>https://philippdubach.com/posts/visualizing-gradients-with-pytorch/</link><pubDate>Sat, 23 Aug 2025 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/visualizing-gradients-with-pytorch/</guid><description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Gradient"&gt;Gradients&lt;/a&gt; are one of the most important concepts in calculus and machine learning, but it&amp;rsquo;s often poorly understood. Trying to understand them better myself, I wanted to build a visualization tool that helps me develop the correct mental picture of what the gradient of a function is. I came across &lt;a href="https://github.com/GistNoesis/VisualizeGradient"&gt;GistNoesis/VisualizeGradient&lt;/a&gt;, so I went on from there to write my own iteration. This mental model generalizes beautifully to higher dimensions and is the foundation for understanding optimization algorithms like gradient descent.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-torch-gradients_Figure_2-png-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/torch-gradients_Figure_2.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/torch-gradients_Figure_2.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/torch-gradients_Figure_2.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/torch-gradients_Figure_2.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/torch-gradients_Figure_2.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/torch-gradients_Figure_2.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/torch-gradients_Figure_2.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/torch-gradients_Figure_2.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/torch-gradients_Figure_2.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/torch-gradients_Figure_2.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/torch-gradients_Figure_2.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/torch-gradients_Figure_2.png"
alt="2D Gradient Plot: The colored surface shows function values. Black arrows show gradient vectors in the input plane (x-y space), pointing toward the direction of steepest ascent."
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-torch-gradients_Figure_2-png-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/torch-gradients_Figure_2.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="2D Gradient Plot: The colored surface shows function values. Black arrows show gradient vectors in the input plane (x-y space), pointing toward the direction of steepest ascent." decoding="async"&gt;
&lt;/dialog&gt;
&lt;em&gt;The colored surface shows function values. Black arrows show gradient vectors in the input plane (x-y space), pointing toward the direction of steepest ascent.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you are interested in having a closer look or replicating my approach, the full project can be found on my &lt;a href="https://github.com/philippdubach/torch-gradients/"&gt;GitHub&lt;/a&gt;. I&amp;rsquo;m also looking forward to doing something similar on the &lt;a href="https://blog.foletta.net/post/2025-07-14-clt/"&gt;Central Limit Theorem&lt;/a&gt; as well as doing a short tutorial on &lt;a href="https://static.philippdubach.com/opt_vol_surface_plot_fig1.png"&gt;plotting options volatility surfaces with python&lt;/a&gt;, a project I have been waiting to finish for some time now.&lt;/p&gt;</description></item><item><title>Counting Cards with Computer Vision</title><link>https://philippdubach.com/posts/counting-cards-with-computer-vision/</link><pubDate>Sun, 06 Jul 2025 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/counting-cards-with-computer-vision/</guid><description>&lt;p&gt;After installing &lt;a href="https://www.anthropic.com/claude-code"&gt;Claude Code&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster through natural language commands&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was looking for a task to test its abilities. Fairly quickly we wrote &lt;a href="https://gist.github.com/philippdubach/741cbd56498e43375892966ca691b9c2"&gt;less than 200 lines of python code predicting blackjack odds&lt;/a&gt; using Monte Carlo simulation. When I went on to test this little tool on &lt;a href="https://games.washingtonpost.com/games/blackjack"&gt;Washington Post&amp;rsquo;s&lt;/a&gt; online blackjack (I also didn&amp;rsquo;t know that existed!) I quickly noticed how impractical it was to manually input all the card values on the table. What if the tool could also handle blackjack card detection automatically and calculate the odds from it? I have never done anything with computer vision so this seemed like a good challenge.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-classification-gif-1" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/classification.gif 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/classification.gif 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/classification.gif 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/classification.gif 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/classification.gif 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/classification.gif 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/classification.gif 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/classification.gif 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/classification.gif 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/classification.gif 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/classification.gif 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/classification.gif"
alt="alt text here"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-classification-gif-1" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/classification.gif"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="alt text here" decoding="async"&gt;
&lt;/dialog&gt;
To get to any reasonable result we have to start with classification where we &amp;ldquo;teach&amp;rdquo; the model to categorize data by showing them lots of examples with correct labels. But where do the labels come from? I manually annotated &lt;a href="https://universe.roboflow.com/cards-agurd/playing_card_classification"&gt;409 playing cards across 117 images&lt;/a&gt; using Roboflow Annotate (at first I only did half as much - why this wasn&amp;rsquo;t a good idea we&amp;rsquo;ll see in a minute). Once enough screenshots of cards were annotated we can train the model to recognize the cards and predict card values on tables it has never seen before. I was able to use a &lt;a href="https://www.nvidia.com/en-us/data-center/tesla-t4/"&gt;NVIDIA T4 GPU&lt;/a&gt; inside Google Colab which offers some GPU time for free when capacity is available.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-gpu_setup_colab-png-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/gpu_setup_colab.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/gpu_setup_colab.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/gpu_setup_colab.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/gpu_setup_colab.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/gpu_setup_colab.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/gpu_setup_colab.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/gpu_setup_colab.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/gpu_setup_colab.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/gpu_setup_colab.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/gpu_setup_colab.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/gpu_setup_colab.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/gpu_setup_colab.png"
alt="alt text here"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-gpu_setup_colab-png-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/gpu_setup_colab.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="alt text here" decoding="async"&gt;
&lt;/dialog&gt;
During training, the algorithm learns patterns from this example data, adjusting its internal parameters millions of times until it gets really good at recognizing the differences between categories (in this case different cards). Once trained, the model can then make predictions on new, unseen data by applying the patterns it learned. With the annotated dataset ready, it was time to implement the actual computer vision model. I chose to run inference on &lt;a href="https://docs.ultralytics.com/de/models/yolo11/"&gt;Ultralytics&amp;rsquo; YOLOv11&lt;/a&gt; pre-trained model, a leading object detection algorithm. I set up the environment in Google Colab following the &lt;a href="https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/train-yolo11-object-detection-on-custom-dataset.ipynb"&gt;&amp;ldquo;How to Train YOLO11 Object Detection on a Custom Dataset&amp;rdquo;&lt;/a&gt; notebook. After extracting the annotated dataset from Roboflow, I began training the model using the pre-trained YOLOv11s weights as a starting point. This approach, called &lt;a href="https://en.wikipedia.org/wiki/Transfer_learning"&gt;transfer learning&lt;/a&gt;, allows the model to reuse patterns already learned from millions of general images and adapt them to this specific task.
I initially set it up to &lt;a href="https://docs.ultralytics.com/guides/model-training-tips/#other-techniques-to-consider-when-handling-a-large-dataset"&gt;run for 350 epochs&lt;/a&gt;, though the model&amp;rsquo;s built-in early stopping mechanism kicked in after 242 epochs when no improvement was observed for 100 consecutive epochs. The best results were achieved at epoch 142, taking around 13 minutes to complete on the Tesla T4 GPU.
The initial results were quite promising, with an overall mean Average Precision (mAP) of 80.5% at IoU threshold 0.5. Most individual card classes achieved good precision and recall scores, with only a few cards like the 6 and Queen showing slightly lower precision values.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-run1_results-png-3" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/run1_results.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/run1_results.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/run1_results.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/run1_results.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run1_results.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/run1_results.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/run1_results.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/run1_results.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run1_results.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/run1_results.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/run1_results.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run1_results.png"
alt="Training results showing confusion matrix and loss curves"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-run1_results-png-3" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/run1_results.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Training results showing confusion matrix and loss curves" decoding="async"&gt;
&lt;/dialog&gt;
However, looking at the confusion matrix and loss curves revealed some interesting patterns. While the model was learning effectively (as shown by the steadily decreasing loss), there were still some misclassifications between similar cards, particularly among the numbered cards. This highlighted exactly why I mentioned earlier that annotating only half the amount of data initially &amp;ldquo;wasn&amp;rsquo;t a good idea&amp;rdquo; - more training examples would likely improve these edge cases and reduce confusion between similar-looking cards. My first attempt at solving the remaining accuracy issues was to add another layer to the workflow by sending the detected cards to Anthropic&amp;rsquo;s Claude API for additional OCR processing.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-claude_vision_workflow_results-png-4" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/claude_vision_workflow_results.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/claude_vision_workflow_results.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/claude_vision_workflow_results.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/claude_vision_workflow_results.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/claude_vision_workflow_results.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/claude_vision_workflow_results.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/claude_vision_workflow_results.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/claude_vision_workflow_results.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/claude_vision_workflow_results.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/claude_vision_workflow_results.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/claude_vision_workflow_results.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/claude_vision_workflow_results.png"
alt="Roboflow workflow with Claude API integration"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-claude_vision_workflow_results-png-4" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/claude_vision_workflow_results.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Roboflow workflow with Claude API integration" decoding="async"&gt;
&lt;/dialog&gt;
This hybrid approach was very effective - the combination of YOLO&amp;rsquo;s object detection to dynamically crop down the Black Jack table to individual cards with Claude&amp;rsquo;s advanced vision capabilities yielded 99.9% accuracy on the predicted cards. However, this solution came with a significant drawback: the additional API layer consumed valuable time and the large model&amp;rsquo;s processing overhead, making it impractical for real-time gameplay.&lt;/p&gt;
&lt;p&gt;Seeking a faster solution, I implemented the same workflow &lt;a href="https://github.com/JaidedAI/EasyOCR"&gt;locally using easyOCR&lt;/a&gt; instead. EasyOCR seems to be really good at extracting black text on white background but &lt;a href="https://stackoverflow.com/questions/68261703/how-to-improve-accuracy-prediction-for-easyocr"&gt;might struggle with everything else&lt;/a&gt;. While it was able to correctly identify the card numbers when it detected them, it struggled to recognize around half of the cards in the first place - even when fed pre-cropped card images directly from the YOLO model. This inconsistency made it unreliable for the application.
Rather than continue band-aid solutions, I decided to go back and improve my dataset. I doubled the training data by adding another 60 screenshots with the same train/test split as before. More importantly, I went through all the previous annotations and fixed many of the bounding polygons. I noticed that several misidentifications were caused by the model detecting face-down dealer cards as valid cards, which happened because some annotations for face-up cards inadvertently included parts of the card backs next to them. The improved dataset and cleaned annotations delivered what I was hoping for: The confusion matrix now shows a much cleaner diagonal pattern, indicating that the model now correctly identifies most cards without the cross-contamination issues we saw earlier.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-run_best-png-6" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/run_best.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/run_best.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/run_best.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/run_best.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run_best.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/run_best.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/run_best.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/run_best.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run_best.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/run_best.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/run_best.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/run_best.png"
alt="Final training results with improved dataset"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-run_best-png-6" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/run_best.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Final training results with improved dataset" decoding="async"&gt;
&lt;/dialog&gt;
Both the training and validation losses converge smoothly without signs of overfitting, while the precision and recall metrics climb steadily to plateau near perfect scores. The mAP@50 reaches an impressive 99.5%. Most significantly, the confusion matrix now shows that the model has virtually eliminated false positives with background elements. The &amp;ldquo;background&amp;rdquo; column (rightmost) in the confusion matrix is now much cleaner, with only minimal misclassifications of actual cards as background noise.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-local_run_interference_visual-png-7" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/local_run_interference_visual.png 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/local_run_interference_visual.png 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/local_run_interference_visual.png 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/local_run_interference_visual.png 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/local_run_interference_visual.png 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/local_run_interference_visual.png 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/local_run_interference_visual.png 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/local_run_interference_visual.png 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/local_run_interference_visual.png 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/local_run_interference_visual.png 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/local_run_interference_visual.png 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/local_run_interference_visual.png"
alt="Real-time blackjack card detection and odds calculation"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-local_run_interference_visual-png-7" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/local_run_interference_visual.png"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Real-time blackjack card detection and odds calculation" decoding="async"&gt;
&lt;/dialog&gt;
With the model trained and performing, it was time to deploy it and play some blackjack. Initially, I tested the system using &lt;a href="https://docs.roboflow.com/deploy/serverless-hosted-api-v2"&gt;Roboflow&amp;rsquo;s hosted API&lt;/a&gt;, which took around 4 seconds per inference - far too slow for practical gameplay. However, running the model locally on my laptop dramatically improved performance, achieving inference times of less than 0.1 seconds per image (1.3ms preprocess, 45.5ms inference, 0.4ms postprocess per image). I then &lt;a href="https://python-mss.readthedocs.io/"&gt;integrated the model with MSS&lt;/a&gt; to capture a real-time feed of my browser window. The system automatically overlays the detected cards with their predicted values and confidence scores
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-black_jack_odds_demo-gif-8" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/black_jack_odds_demo.gif 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/black_jack_odds_demo.gif 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/black_jack_odds_demo.gif 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/black_jack_odds_demo.gif 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/black_jack_odds_demo.gif 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/black_jack_odds_demo.gif 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/black_jack_odds_demo.gif 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/black_jack_odds_demo.gif 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/black_jack_odds_demo.gif 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/black_jack_odds_demo.gif 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/black_jack_odds_demo.gif 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/black_jack_odds_demo.gif"
alt="Overview of selected fitted curves"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-black_jack_odds_demo-gif-8" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/black_jack_odds_demo.gif"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Overview of selected fitted curves" decoding="async"&gt;
&lt;/dialog&gt;
The final implementation successfully combines the pieces: the computer vision model detects and identifies cards in real-time, feeds this information to the Monte Carlo simulation, and displays both the card recognition results and the calculated odds directly on screen - do not try this at your local (online) casino!&lt;/p&gt;</description></item><item><title>Modeling Glycemic Response with XGBoost</title><link>https://philippdubach.com/posts/modeling-glycemic-response-with-xgboost/</link><pubDate>Fri, 30 May 2025 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/modeling-glycemic-response-with-xgboost/</guid><description>&lt;br&gt;
&lt;p&gt;Earlier this year I wrote how &lt;a href="https://philippdubach.com/posts/i-built-a-cgm-data-reader/"&gt;I built a CGM data reader&lt;/a&gt; after wearing a continuous glucose monitor myself. Since I was already logging my macronutrients and learning more about molecular biology in an &lt;a href="https://ocw.mit.edu/courses/res-7-008-7-28x-molecular-biology/"&gt;MIT MOOC&lt;/a&gt;, I became curious: given a meal&amp;rsquo;s macronutrients (carbs, protein, fat) and some basic individual characteristics (age, BMI), could a machine learning model predict the shape of my postprandial glucose curve? I came across &lt;a href="https://www.cell.com/cell/fulltext/S0092-8674(15)01481-6"&gt;Zeevi et al.&lt;/a&gt;&amp;rsquo;s paper on Personalized Nutrition by Prediction of Glycemic Responses, which used machine learning to predict individual glycemic responses from meal data. Exactly what I had in mind. Unfortunately, neither the data nor the code were publicly available. So I decided to build my own model. In the process I wrote this &lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5914902"&gt;working paper&lt;/a&gt;.&lt;/p&gt;
&lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5914902"&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-working_paper_overview-jpg-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/working_paper_overview.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/working_paper_overview.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/working_paper_overview.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/working_paper_overview.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/working_paper_overview.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/working_paper_overview.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/working_paper_overview.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/working_paper_overview.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/working_paper_overview.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/working_paper_overview.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/working_paper_overview.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/working_paper_overview.jpg"
alt="Overview of Working Paper Pages"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-working_paper_overview-jpg-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/working_paper_overview.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Overview of Working Paper Pages" decoding="async"&gt;
&lt;/dialog&gt;
&lt;/a&gt;
&lt;p&gt;The paper documents my attempt to build an open, reproducible glucose prediction pipeline, and what I learned about why that is harder than it sounds. The methodologies employed were largely inspired by &lt;a href="https://www.cell.com/cell/fulltext/S0092-8674(15)01481-6?_returnURL=https%3A%2F%2Flinkinghub.elsevier.com%2Fretrieve%2Fpii%2FS0092867415014816%3Fshowall%3Dtrue"&gt;Zeevi et al.&lt;/a&gt;&amp;rsquo;s approach. This matters because the landscape of personalized nutrition is increasingly dominated by proprietary systems. Companies like ZOE, DayTwo, and Ultrahuman all run versions of this pipeline on closed data. Open-source alternatives remain scarce.&lt;/p&gt;
&lt;h2 id="why-not-only-use-my-own-data"&gt;Why not only use my own data?&lt;/h2&gt;
&lt;p&gt;I quickly realized that training a model only on my own CGM data was not going to work. Over several weeks of diligent logging, I collected roughly 40 meal-response pairs. To make matters worse, &lt;a href="https://doi.org/10.1093/ajcn/nqaa198"&gt;Howard, Guo &amp;amp; Hall (2020)&lt;/a&gt; showed that two CGMs worn simultaneously on the same person can give discordant meal rankings for postprandial glucose, meaning some of the variance in the signal is measurement noise, not biology.&lt;/p&gt;
&lt;p&gt;To get enough data, I used the publicly available &lt;a href="https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.2005143"&gt;Hall dataset&lt;/a&gt; containing continuous glucose monitoring data from 57 adults, which I narrowed down to 112 standardized meals from 19 non-diabetic subjects with their respective glucose curve after the meal (full methodology in the paper).&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-cgm-workflow-graph-jpg-1" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/cgm-workflow-graph.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/cgm-workflow-graph.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/cgm-workflow-graph.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/cgm-workflow-graph.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-workflow-graph.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/cgm-workflow-graph.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/cgm-workflow-graph.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/cgm-workflow-graph.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-workflow-graph.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/cgm-workflow-graph.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/cgm-workflow-graph.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-workflow-graph.jpg"
alt="Overview of the CGM pipeline workflow"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-cgm-workflow-graph-jpg-1" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/cgm-workflow-graph.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Overview of the CGM pipeline workflow" decoding="async"&gt;
&lt;/dialog&gt;
&lt;h2 id="gaussian-curve-fitting"&gt;Gaussian curve fitting&lt;/h2&gt;
&lt;p&gt;Rather than trying to predict the entire glucose curve, I simplified the problem by fitting each postprandial response to a normalized Gaussian function. This gave me three key parameters to predict: amplitude (how high glucose rises), time-to-peak (when it peaks), and curve width (how long the response lasts).&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-cgm-fitted-curve-large1-jpg-2" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/cgm-fitted-curve-large1.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/cgm-fitted-curve-large1.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/cgm-fitted-curve-large1.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/cgm-fitted-curve-large1.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-fitted-curve-large1.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/cgm-fitted-curve-large1.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/cgm-fitted-curve-large1.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/cgm-fitted-curve-large1.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-fitted-curve-large1.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/cgm-fitted-curve-large1.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/cgm-fitted-curve-large1.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-fitted-curve-large1.jpg"
alt="Overview of single fitted curve of cgm measurements"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-cgm-fitted-curve-large1-jpg-2" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/cgm-fitted-curve-large1.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Overview of single fitted curve of cgm measurements" decoding="async"&gt;
&lt;/dialog&gt;
&lt;p&gt;The Gaussian approximation worked surprisingly well for characterizing most glucose responses. While some curves fit better than others, the majority of postprandial responses were well-captured, though there is clear variation between individuals and meals. Some responses were high amplitude, narrow width, while others are more gradual and prolonged.&lt;/p&gt;
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-example-fitted-cgm-measurements-jpg-3" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/example-fitted-cgm-measurements.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/example-fitted-cgm-measurements.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/example-fitted-cgm-measurements.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/example-fitted-cgm-measurements.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/example-fitted-cgm-measurements.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/example-fitted-cgm-measurements.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/example-fitted-cgm-measurements.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/example-fitted-cgm-measurements.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/example-fitted-cgm-measurements.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/example-fitted-cgm-measurements.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/example-fitted-cgm-measurements.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/example-fitted-cgm-measurements.jpg"
alt="Overview of selected fitted curves"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-example-fitted-cgm-measurements-jpg-3" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/example-fitted-cgm-measurements.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Overview of selected fitted curves" decoding="async"&gt;
&lt;/dialog&gt;
&lt;h2 id="xgboost-pipeline"&gt;XGBoost pipeline&lt;/h2&gt;
&lt;p&gt;I then trained an XGBoost regressor with 27 engineered features including meal composition, participant characteristics, and interaction terms. XGBoost was chosen for its ability to handle mixed data types, built-in feature importance, and strong performance on tabular data. The pipeline included hyperparameter tuning with 5-fold cross-validation to optimize learning rate, tree depth, and regularization parameters. Rather than relying solely on basic meal macronutrients, I engineered features across multiple categories and implemented CGM statistical features calculated over different time windows (24-hour and 4-hour periods), including time-in-range and glucose variability metrics. Architecture-wise, I trained three separate XGBoost regressors, one for each Gaussian parameter.&lt;/p&gt;
&lt;h2 id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;The model could predict &lt;em&gt;how high&lt;/em&gt; my blood sugar rises after a meal with moderate accuracy (R² = 0.46, correlation = 0.73, p &amp;lt; 0.001). Not good enough for clinical guidance, which typically requires R² &amp;gt; 0.7, but meaningfully better than the multi-linear regression baseline (R² = 0.24).&lt;/p&gt;
&lt;p&gt;The more telling result is what the model could not do. It had no idea &lt;em&gt;when&lt;/em&gt; blood sugar would peak. The time-to-peak prediction was literally worse than guessing the average every time (R² = -0.76, p = 0.896). Curve width prediction was marginally better but still not useful (R² = 0.10). In other words: meal composition tells you something about the magnitude of your glucose spike, but almost nothing about its timing or duration. That is a meaningful finding in itself, consistent with the idea that temporal dynamics are driven by factors like gastric emptying, insulin sensitivity, and gut microbiome composition, none of which were captured in the feature set.&lt;/p&gt;
&lt;p&gt;For context, &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S1746809423012429"&gt;Cappon et al. (2023)&lt;/a&gt; trained a similar XGBRegressor on 3,296 meals from 927 healthy individuals and achieved a correlation of r = 0.48 for predicting glycemic response magnitude. Their larger dataset did not dramatically improve over my amplitude correlation of 0.73, but they also found systematic bias in predictions, suggesting that XGBoost captures the general direction well while missing individual-level variation. Separately, &lt;a href="https://www.nature.com/articles/s41598-025-01367-7"&gt;Shin et al. (2025)&lt;/a&gt; tried a bidirectional LSTM on 171 healthy adults and achieved r = 0.43, worse than XGBoost on amplitude. Deep learning does not automatically win here, especially at small-to-medium dataset sizes. Data quantity matters more than model complexity.&lt;/p&gt;
&lt;p&gt;A study on &lt;a href="https://www.nature.com/articles/s41522-025-00650-9"&gt;glycemic prediction in pregnant women&lt;/a&gt; found that adding gut microbiome data increased explained variance in glucose peaks from 34% to 42%, underscoring that meal composition alone leaves a lot on the table.&lt;/p&gt;
&lt;p&gt;The complete code, Jupyter notebooks, processed datasets, and supplementary results are available in my &lt;a href="https://github.com/philippdubach/glucose-response-analysis"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;(10/06/2025) Update: Today I came across Marcel Salathé&amp;rsquo;s &lt;a href="https://www.linkedin.com/posts/salathe_myfoodrepo-digitalhealth-precisionnutrition-activity-7337806988082393088-2Lsu?utm_source=share&amp;amp;utm_medium=member_ios&amp;amp;rcm=ACoAADeInT4BJMhtg5DSjxX1jVtIAs5w_KxZm-g"&gt;LinkedIn post&lt;/a&gt; on a publication out of EPFL: &lt;a href="https://www.frontiersin.org/journals/nutrition/articles/10.3389/fnut.2025.1539118/full"&gt;Personalized glucose prediction using in situ data only&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;With data from over 1,000 participants of the Food &amp;amp; You digital cohort, we show that a machine learning model using only food data from myFoodRepo and a glucose monitor can closely track real blood sugar responses to any meal (correlation of 0.71).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;As expected, Singh et al. achieve substantially better predictive performance (R = 0.71 vs R² = 0.46). The most critical difference is sample size: their 1,000+ participants versus my 19 (from the &lt;a href="https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.2005143"&gt;Hall dataset&lt;/a&gt;). They leveraged the &lt;a href="https://pubmed.ncbi.nlm.nih.gov/38033170/"&gt;&amp;ldquo;Food &amp;amp; You&amp;rdquo; study&lt;/a&gt; with high-resolution nutritional intake data from more than 46 million kcal collected across 315,126 dishes, 1,470,030 blood glucose measurements, and 1,024 gut microbiota samples. Both studies use XGBoost, SHAP for interpretability, cross-validation for evaluation, and mathematical approaches to characterize glucose responses (Gaussian curve fitting in my case, incremental AUC in theirs). The methodological overlap is reassuring; what separates the results is data at scale.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The &lt;a href="https://www.nature.com/articles/s41597-025-05851-7"&gt;CGMacros dataset&lt;/a&gt; (Das et al., Scientific Data, 2025) now provides the first publicly available multimodal dataset with CGM readings, food macronutrients, meal photos, activity data, and microbiome profiles for 45 participants. It even includes an XGBoost example script for predicting postprandial AUC. This is exactly the kind of open resource the field needs more of.&lt;/em&gt;&lt;a id="update"&gt;&lt;/p&gt;
&lt;aside class="disclaimer" role="note" aria-label="Disclaimer"&gt;
&lt;div class="disclaimer-content"&gt;&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; For informational purposes only, not medical advice. Consult a qualified healthcare provider for any medical questions or conditions.&lt;/p&gt;&lt;/div&gt;
&lt;/aside&gt;</description></item><item><title>I Built a CGM Data Reader</title><link>https://philippdubach.com/posts/i-built-a-cgm-data-reader/</link><pubDate>Thu, 02 Jan 2025 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/i-built-a-cgm-data-reader/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you&amp;rsquo;re reading this, you might also be interested in: &lt;a href="https://philippdubach.com/posts/modeling-glycemic-response-with-xgboost/"&gt;Modeling Glycemic Response with XGBoost&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Last year I put a Continuous Glucose Monitor (CGM) sensor, specifically the &lt;a href="https://www.freestyle.abbott"&gt;Abbott Freestyle Libre 3&lt;/a&gt;, on my left arm. Why? I wanted to optimize my nutrition for endurance cycling competitions. Where I live, the sensor is easy to get—without any medical prescription—and even easier to use. Unfortunately, Abbott&amp;rsquo;s &lt;a href="https://apps.apple.com/us/app/freestyle-librelink-us/id1325992472"&gt;FreeStyle LibreLink&lt;/a&gt; app is less than optimal (3,250 other people with an average rating of 2.9/5.0 seem to agree). In their defense, the web app LibreView does offer some nice reports which can be generated as PDFs—not very dynamic, but still something! What I had in mind was more in the fashion of the &lt;a href="https://ultrahuman.com/m1"&gt;Ultrahuman M1 dashboard&lt;/a&gt;. Unfortunately, I wasn&amp;rsquo;t allowed to use my Libre sensor (EU firmware) with their app (yes, I spoke to customer service).&lt;/p&gt;
&lt;p&gt;At that point, I wasn&amp;rsquo;t left with much enthusiasm, only a coin-sized sensor in my arm. The LibreView website fortunately lets you download most of your (own) data in a CSV report (&lt;em&gt;there is also a &lt;a href="https://github.com/FokkeZB/libreview-unofficial"&gt;reverse engineered API&lt;/a&gt;&lt;/em&gt;), which is nice. So that&amp;rsquo;s what I did: download the data, &lt;code&gt;pd.read_csv()&lt;/code&gt; it into my notebook, calculate summary statistics, and plot the values.
&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-libre-measurements-jpg-0" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/libre-measurements.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/libre-measurements.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/libre-measurements.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/libre-measurements.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/libre-measurements.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/libre-measurements.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/libre-measurements.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/libre-measurements.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/libre-measurements.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/libre-measurements.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/libre-measurements.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/libre-measurements.jpg"
alt="Visualized CGM Datapoints"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-libre-measurements-jpg-0" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/libre-measurements.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Visualized CGM Datapoints" decoding="async"&gt;
&lt;/dialog&gt;
After some interpolation, I now had the same view as the LibreLink app (which I had rejected earlier) provided. Yet, this setup allowed me to do further analysis and visualizations by adding other datapoints (workouts, sleep, nutrition) I was also collecting at that time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Blood sugar from &lt;a href="https://www.libreview.com/"&gt;LibreView&lt;/a&gt;: Measurement timestamps + glucose values&lt;/li&gt;
&lt;li&gt;Nutrition from &lt;a href="https://macrofactorapp.com/"&gt;MacroFactor&lt;/a&gt;: Meal timestamps + macronutrients (carbs, protein, and fat)&lt;/li&gt;
&lt;li&gt;Sleep data from &lt;a href="https://sleepcycle.com/"&gt;Sleep Cycle&lt;/a&gt;: Sleep start timestamp + time in bed + time asleep (+ sleep quality, which is a proprietary measure calculated by the app)&lt;/li&gt;
&lt;li&gt;Cardio workouts from &lt;a href="https://connect.garmin.com/"&gt;Garmin&lt;/a&gt;: Workout start timestamp + workout duration&lt;/li&gt;
&lt;li&gt;Strength workouts from &lt;a href="https://www.hevyapp.com/"&gt;Hevy&lt;/a&gt;: Workout start timestamp + workout duration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class="post-figure" style="width: 80%; margin: 1.5rem auto;"&gt;
&lt;button type="button" class="img-trigger" data-lightbox-target="lightbox-cgm-dashboard-jpg-1" aria-label="View full-size image"&gt;
&lt;picture class="img-lightbox"&gt;
&lt;source media="(max-width: 768px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=320,quality=80,format=auto/cgm-dashboard.jpg 320w,
https://static.philippdubach.com/cdn-cgi/image/width=480,quality=80,format=auto/cgm-dashboard.jpg 480w,
https://static.philippdubach.com/cdn-cgi/image/width=640,quality=80,format=auto/cgm-dashboard.jpg 640w,
https://static.philippdubach.com/cdn-cgi/image/width=960,quality=80,format=auto/cgm-dashboard.jpg 960w,
https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-dashboard.jpg 1200w"
sizes="80vw"&gt;
&lt;source media="(max-width: 1024px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=768,quality=80,format=auto/cgm-dashboard.jpg 768w,
https://static.philippdubach.com/cdn-cgi/image/width=1024,quality=80,format=auto/cgm-dashboard.jpg 1024w,
https://static.philippdubach.com/cdn-cgi/image/width=1440,quality=80,format=auto/cgm-dashboard.jpg 1440w"
sizes="80vw"&gt;
&lt;source media="(min-width: 1025px)"
srcset="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-dashboard.jpg 1200w,
https://static.philippdubach.com/cdn-cgi/image/width=1600,quality=80,format=auto/cgm-dashboard.jpg 1600w,
https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=80,format=auto/cgm-dashboard.jpg 2000w"
sizes="80vw"&gt;
&lt;img src="https://static.philippdubach.com/cdn-cgi/image/width=1200,quality=80,format=auto/cgm-dashboard.jpg"
alt="Final Dashboard"
class=""
width="1200"
loading="lazy"
decoding="async"&gt;
&lt;/picture&gt;
&lt;/button&gt;
&lt;/figure&gt;
&lt;dialog id="lightbox-cgm-dashboard-jpg-1" class="lightbox-dialog" aria-label="Full-size image" data-hires="https://static.philippdubach.com/cdn-cgi/image/width=2000,quality=85,format=auto/cgm-dashboard.jpg"&gt;
&lt;form method="dialog" class="lightbox-close-form"&gt;
&lt;button type="submit" class="lightbox-close" aria-label="Close"&gt;×&lt;/button&gt;
&lt;/form&gt;
&lt;img alt="Final Dashboard" decoding="async"&gt;
&lt;/dialog&gt;
After structuring those datapoints in a dataframe and normalizing timestamps, I was able to quickly highlight sleep (blue boxes with callouts for time in bed, time asleep, and sleep quality) and workouts (red traces on glucose measurements for strength workouts, green traces for cardio workouts) by plotting highlighted traces on top of the historic glucose trail for a set period. Furthermore, I was able to add annotations for nutrition events with the respective macronutrients.&lt;/p&gt;
&lt;p&gt;I asked Claude to create some sample data and streamline the functions to reduce dependencies on the specific data sources I used. The resulting notebook is a comprehensive CGM data analysis tool that loads and processes glucose readings alongside lifestyle data (nutrition, workouts, and sleep), then creates an integrated dashboard for visualization. The code handles data preprocessing including interpolation of missing glucose values, timeline synchronization across different data sources, and statistical analysis with key metrics like time-in-range and coefficient of variation. The main output is a day-by-day dashboard that overlays workout periods, nutrition events, and sleep phases onto continuous glucose monitoring data, enabling users to identify patterns and correlations between lifestyle factors and blood sugar responses.&lt;/p&gt;
&lt;p&gt;You can find the complete &lt;a href="https://github.com/philippdubach/glucose-tracker/blob/fd5992961cfb4630dad439c782430190937414a3/notebooks/data_exploration.ipynb"&gt;notebook&lt;/a&gt; as well as the sample data in my &lt;a href="https://github.com/philippdubach/glucose-tracker/"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The Tech behind this Site</title><link>https://philippdubach.com/posts/the-tech-behind-this-site/</link><pubDate>Mon, 15 Jan 2024 00:00:00 +0000</pubDate><author>me@philippdubach.com (Philipp D. Dubach)</author><guid>https://philippdubach.com/posts/the-tech-behind-this-site/</guid><description>&lt;p&gt;This site runs on Hugo, deployed to GitHub Pages with Cloudflare CDN. Images are hosted on R2 (&lt;code&gt;static.philippdubach.com&lt;/code&gt;) with automatic resizing and WebP conversion.&lt;/p&gt;
&lt;p&gt;The core challenge was responsive images. Standard markdown &lt;code&gt;![alt](url)&lt;/code&gt; doesn&amp;rsquo;t support multiple sizes. I built a &lt;a href="https://gist.github.com/philippdubach/167189c7090c6813c5110c467cb5ebe9"&gt;Hugo shortcode&lt;/a&gt; that generates &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; elements with breakpoint-specific sources—upload once at full quality, serve optimized versions (320px mobile to 1600px desktop) automatically.&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;May 2026&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Cron Reliability&lt;/em&gt; — Scheduled rebuilds moved off GitHub Actions cron (drifts 30+ min, occasionally skips during platform incidents) onto a Cloudflare Worker that fires &lt;code&gt;workflow_dispatch&lt;/code&gt; on a deterministic &lt;code&gt;17 */3 * * *&lt;/code&gt; UTC schedule. Builds still run on Actions; only the trigger moved. Cadence increased from 3× daily to every 3 hours, so max publish delay for future-dated posts dropped from ~7h to ~3h. Worker source: &lt;code&gt;social-automation/build-trigger/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hugo 0.161.1 Upgrade&lt;/em&gt; — Bumped from 0.157.0. Byte-identical output, zero deprecation warnings. Added a local diff harness (&lt;code&gt;scripts/upgrade-diff.sh&lt;/code&gt;) that builds with two Hugo versions and diffs &lt;code&gt;public/&lt;/code&gt;, used to validate the bump as a no-op. The new release window opened up &lt;code&gt;strings.ReplacePairs&lt;/code&gt; (collapsed an 8-call entity-decode chain in &lt;code&gt;llms-full.txt&lt;/code&gt;) and fixed enough Goldmark / &lt;code&gt;RenderShortcodes&lt;/code&gt; edge cases that 8 of 9 post-render regex passes in the markdown variant template could go. One was actively harmful: the math-delimiter regex was corrupting Wikipedia URLs like &lt;code&gt;Universal_Serial_Bus_\(USB\)&lt;/code&gt; into &lt;code&gt;_$USB$_&lt;/code&gt; because the pattern was over-eager.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Index Redesign&lt;/em&gt; — Articles and projects now share the same structure: hero dropped, the featured row &lt;em&gt;is&lt;/em&gt; the masthead (red overline, 1.5px red rule, large headline), hairline divider, filter chips reframed as &amp;ldquo;browse the archive&amp;rdquo; rather than page nav. Quiet &lt;code&gt;&amp;lt;h1 class=&amp;quot;page-label&amp;quot;&amp;gt;&lt;/code&gt; for SEO and screen readers without competing visually with the featured headline. Featured card image now requests a 1200×630 landscape source matching the CSS aspect-ratio (was getting a 480×600 portrait stretched sideways, which clipped faces).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Markdown Variant Maturity&lt;/em&gt; — &lt;code&gt;/posts/&amp;lt;slug&amp;gt;/index.md&lt;/code&gt; now emits clean markdown via output-format-aware sibling shortcodes (&lt;code&gt;img.markdown.md&lt;/code&gt;, &lt;code&gt;disclaimer.markdown.md&lt;/code&gt;, &lt;code&gt;newsletter.markdown.md&lt;/code&gt;, &lt;code&gt;readnext.markdown.md&lt;/code&gt;). HTML pollution never enters the markdown stream. YAML preamble parseable by gray-matter and every major LLM tooling SDK. Dropped the &lt;code&gt;eq .Section &amp;quot;posts&amp;quot;&lt;/code&gt; gate so &lt;code&gt;/about/&lt;/code&gt;, &lt;code&gt;/research/&lt;/code&gt;, &lt;code&gt;/subscribe/&lt;/code&gt; ship real markdown content too.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Worker Audit&lt;/em&gt; — Three parallel reviews (perf, reliability, security) of the Hugo site and remaining Cloudflare Workers found nine issues worth fixing, clustered in the social-automation workers. Bluesky&amp;rsquo;s 300-char post limit was being silently exceeded when the appended URL pushed total length over budget, leaving stuck-loop posts that retried every 15 minutes. The &lt;code&gt;og:image&lt;/code&gt; URL was being fetched without domain validation (SSRF gap, low probability). Cron ticks could race the same article when a tick took longer than 15 minutes. The bluesky worker fetched each article URL twice per post. All four fixed, plus a GoatCounter title sanitizer at the Worker boundary so a downstream consumer that uses &lt;code&gt;innerHTML&lt;/code&gt; can&amp;rsquo;t be tricked into executing script regardless of how the rendering side is written.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Template Hardening&lt;/em&gt; — &lt;code&gt;readnext&lt;/code&gt; shortcode now emits a build-time &lt;code&gt;warnf&lt;/code&gt; when the slug doesn&amp;rsquo;t match a post, instead of silently rendering nothing. FAQ aggregation extracted to a cached partial (one scan per category instead of two on every faq/* page). Homepage and projects &lt;code&gt;ItemList&lt;/code&gt; JSON-LD capped at 20 entries (Google rich-result band). &lt;code&gt;posts.json&lt;/code&gt; prefers &lt;code&gt;.Description&lt;/code&gt; over &lt;code&gt;.Summary&lt;/code&gt; to skip 86 of 87 markdown renders at build time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Decommissioning&lt;/em&gt; — Removed the post composer (&lt;code&gt;post-composer.pages.dev&lt;/code&gt;) and URL shortener (&lt;code&gt;pdub.click&lt;/code&gt;). Both unused. Deleted source from the repo, then deleted the Pages project, KV namespaces, D1 database via wrangler.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Accessibility&lt;/em&gt; — Sidebar wordmark &lt;code&gt;aria-label&lt;/code&gt; now starts with the visible text per WCAG 2.5.3 (voice-control users saying &amp;ldquo;click philippdubach&amp;rdquo; can now activate it). Newsletter card meta switched to &lt;code&gt;--text-secondary&lt;/code&gt; for 7.0:1 contrast on the pink tint (was 3.91:1, below AA).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Agent Readiness&lt;/em&gt; — Shipped three coordinated changes so AI agents and content-aware crawlers can discover and consume the site through standardized protocols. Every response now carries a &lt;code&gt;Link:&lt;/code&gt; header (RFC 8288) advertising machine-readable resources: the api-catalog, sitemap, RSS and JSON feeds, &lt;code&gt;llms.txt&lt;/code&gt;, and a per-page markdown alternate. A new &lt;code&gt;/.well-known/api-catalog&lt;/code&gt; endpoint returns an RFC 9264 Linkset enumerating those endpoints (RFC 9727). Content negotiation now works: requesting any page with &lt;code&gt;Accept: text/markdown&lt;/code&gt; returns a markdown variant with &lt;code&gt;Content-Type: text/markdown&lt;/code&gt;, &lt;code&gt;Vary: Accept&lt;/code&gt;, and an &lt;code&gt;x-markdown-tokens&lt;/code&gt; count for LLM context-window planning. The robots.txt declares &lt;code&gt;Content-Signal: search=yes, ai-input=yes, ai-train=yes&lt;/code&gt; per draft-romm-aipref-contentsignals.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Worker Refactor&lt;/em&gt; — The 60-line security-headers Worker grew into four focused modules (&lt;code&gt;accept&lt;/code&gt;, &lt;code&gt;links&lt;/code&gt;, &lt;code&gt;cache&lt;/code&gt;, &lt;code&gt;index&lt;/code&gt;) with 32 unit tests. The &lt;code&gt;cache&lt;/code&gt; module is the interesting bit: Cloudflare&amp;rsquo;s edge cache doesn&amp;rsquo;t honor &lt;code&gt;Vary: Accept&lt;/code&gt; by default, so the Worker uses the Cache API with synthetic keys (&lt;code&gt;?_v=html|md&lt;/code&gt;) to keep HTML and Markdown variants isolated under the same URL. Origin fetches HTML or rewrites to &lt;code&gt;/index.md&lt;/code&gt; based on the client&amp;rsquo;s Accept header.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;March 2026&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Hugo Upgrade&lt;/em&gt; — Upgraded from Hugo v0.128.0 to v0.157.0. Migrated deprecated &lt;code&gt;.Site.AllPages&lt;/code&gt; to &lt;code&gt;.Site.Pages&lt;/code&gt; in the sitemap template and &lt;code&gt;.Site.Data&lt;/code&gt; to &lt;code&gt;site.Data&lt;/code&gt; across navigation, structured data, and research templates. Removed a dead &lt;code&gt;readFile&lt;/code&gt; security config key from &lt;code&gt;hugo.toml&lt;/code&gt;. No breaking changes, zero deprecation warnings.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;February 2026&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Frontmatter Unification&lt;/em&gt; — Converted all 70 YAML frontmatter posts to TOML and added Key Takeaways to all 73 posts. Takeaways render as a visible summary box between the post header and content body, optimized for Generative Engine Optimization (GEO) so AI search engines can extract citation-ready passages.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Design Streamlining&lt;/em&gt; — Unified left-bordered aside components (key takeaways, newsletter CTA, disclaimer) to consistent 3px borders and aligned padding. Established a vertical spacing rhythm across post zones: key takeaways, content body, newsletter CTA, footer divider, related posts. Added breathing room around images (1.5rem padding). Refined key takeaways heading to 0.85rem uppercase label with square bullets.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Homepage Redesign&lt;/em&gt; — Rebuilt the homepage with a tabbed layout (Articles/Projects), year dividers, and thumbnail images served via Cloudflare Image Resizing. Consolidated navigation into a unified sidebar.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Security Headers Worker&lt;/em&gt; — Deployed a dedicated Cloudflare Worker on &lt;code&gt;philippdubach.com/*&lt;/code&gt; that injects HSTS, CSP with &lt;code&gt;frame-ancestors&lt;/code&gt;, COEP, COOP, and &lt;code&gt;Permissions-Policy&lt;/code&gt; headers. GitHub Pages doesn&amp;rsquo;t process &lt;code&gt;_headers&lt;/code&gt; files, so the Worker fills that gap. SHA-pinned all GitHub Actions and added Hugo binary checksum verification in CI.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Machine-Readable Feeds&lt;/em&gt; — Added &lt;a href="https://philippdubach.com/feed.json"&gt;JSON Feed 1.1&lt;/a&gt; alongside RSS, a &lt;a href="https://philippdubach.com/api/posts.json"&gt;Posts API&lt;/a&gt; for programmatic access, and &lt;code&gt;llms.txt&lt;/code&gt;/&lt;code&gt;llms-full.txt&lt;/code&gt; for AI crawler discovery. All output formats configured in &lt;code&gt;hugo.toml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;GoatCounter &amp;ldquo;Most Read&amp;rdquo; API&lt;/em&gt; — Built a Cloudflare Worker proxy that queries the GoatCounter API for the top 10 posts over the past 7 days. The footer&amp;rsquo;s &amp;ldquo;Most Read&amp;rdquo; section now fetches live data from this worker instead of a static list.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;FAQ Section&lt;/em&gt; — New &lt;code&gt;/faq/&lt;/code&gt; section with per-category pages (Finance, AI, Tech, Economics, Medicine). Each post can define &lt;code&gt;faq&lt;/code&gt; entries in frontmatter; Hugo aggregates them into browsable FAQ pages with &lt;code&gt;FAQPage&lt;/code&gt; structured data for search engines.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Readnext Shortcode&lt;/em&gt; — Inline &amp;ldquo;Related&amp;rdquo; link to another post: &lt;code&gt;{{&amp;lt; readnext slug=&amp;quot;post-slug&amp;quot; &amp;gt;}}&lt;/code&gt;. Links are validated against live permalinks at build time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;RSS Feed Fixes&lt;/em&gt; — Stripped lightbox overlay elements from full-content RSS to prevent images appearing twice in feed readers. Added XSLT stylesheet for browser-friendly RSS rendering.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cloudflare Cache Purge&lt;/em&gt; — GitHub Actions deployment now automatically purges the Cloudflare cache after each build.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Research Page&lt;/em&gt; — Dynamic &lt;code&gt;/research/&lt;/code&gt; page pulling publication data from &lt;code&gt;data/research.yaml&lt;/code&gt; with SSRN links, DOIs, and structured data.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;January 2026&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Social Automation &amp;amp; AI Model Upgrade&lt;/em&gt; — Upgraded Workers AI model from Llama 3.1 8B to &lt;strong&gt;Llama 4 Scout 17B&lt;/strong&gt; for better post generation. Added Twitter/X automation worker alongside Bluesky. AI generates neutral, non-clickbait posts with extensive banned word filtering.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;UI/UX Polish&lt;/em&gt; — Fixed mobile footer spacing consistency. Increased homepage post spacing (3.75rem). Disclaimers now only display on individual posts, hidden on homepage. Centered related posts heading.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Content Organization&lt;/em&gt; — Taxonomy system with categories (Finance, AI, Medicine, Tech, Economics) and types (Project, Commentary, Essay, Review). Hugo generates browsable &lt;code&gt;/categories/&lt;/code&gt; and &lt;code&gt;/types/&lt;/code&gt; pages.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer Shortcode&lt;/em&gt; — Six types: finance, medical, general, AI, research, gambling. Syntax: &lt;code&gt;{{&amp;lt; disclaimer type=&amp;quot;finance&amp;quot; &amp;gt;}}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;IndexNow Integration&lt;/em&gt; — Automated submissions via GitHub Actions for faster search engine discovery. Only pings recently changed URLs based on &lt;code&gt;lastmod&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;December 2025&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Code Blocks&lt;/em&gt; — Syntax highlighting via Chroma with line numbers in table layout. GitHub-inspired color theme.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Newsletter System&lt;/em&gt; — &lt;a href="https://philippdubach.com/posts/building-a-no-tracking-newsletter-from-markdown-to-distribution/"&gt;Integrated email subscriptions&lt;/a&gt; via Cloudflare Workers + KV. Welcome emails via Resend.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Security &amp;amp; Performance Audit&lt;/em&gt; — Fixed multiple H1 tags per page. Hardened CSP with &lt;code&gt;frame-ancestors&lt;/code&gt;. Added preconnect hints for external domains. Added &lt;code&gt;seoTitle&lt;/code&gt; frontmatter for long titles.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;November 2025&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Shortcodes&lt;/em&gt; — &lt;a href="https://gist.github.com/philippdubach/b703005536d6030c87e17d21cb0d430b"&gt;HTML table shortcode&lt;/a&gt;. Lightbox support on images.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;June 2025&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;SEO &amp;amp; Math&lt;/em&gt; — &lt;a href="https://gist.github.com/philippdubach/39838f8e9e1b9fb085947a6b92062e0a"&gt;Open Graph integration&lt;/a&gt; for social previews. Per-post keyword management. &lt;a href="https://gist.github.com/philippdubach/42ef6e05f5c44b76ef3f66f27a17c41e"&gt;LaTeX rendering via MathJax 3&lt;/a&gt; (conditional loading).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;May 2025&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Full Rebuild&lt;/em&gt; — Migrated from &lt;a href="https://github.com/hugo-sid/hugo-blog-awesome"&gt;hugo-blog-awesome&lt;/a&gt; fork to fully custom Hugo build.&lt;/p&gt;</description></item></channel></rss>