<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Data Engineering Blog &amp; Second Brain</title>
        <link>https://www.ssp.sh/</link>
        <description>Top-ranked Data Engineering Blog with Genuine Insights into the Data Ecosystem with 1000s of Second Brain notes and an online book on Patterns of Data Engineering, all connected with interactive graphs.</description>
        <generator>Hugo -- gohugo.io</generator><language>en-US</language><managingEditor>hello@sspaeti.com (Simon Späti)</managingEditor>
            <webMaster>hello@sspaeti.com (Simon Späti)</webMaster><copyright>All rights reserved. Sharing of excerpts with proper attribution is encouraged for non-commercial purposes. For commercial use or republication, please contact hello@sspaeti.com.</copyright><lastBuildDate>Sat, 03 Oct 2026 00:00:00 &#43;0000</lastBuildDate>
            <atom:link href="https://www.ssp.sh/index.xml" rel="self" type="application/rss+xml" />
        <item>
    <title>A Year of Linux, a Month of Omarchy Quattro—Part 3</title>
    <link>https://www.ssp.sh/blog/omarchy-linux-quattro/</link>
    <pubDate>Sat, 19 Sep 2026 08:40:00 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/omarchy-linux-quattro/</guid><enclosure url="https://www.ssp.sh/blog/omarchy-linux-quattro/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>This is my Part 3 about [[Omarchy]], after using it for more than a year full time, as my main daily driver first on Lenovo ThinkBook 14 AMD, and now <a href="https://x.com/sspaeti/status/1966052590988476910" target="_blank" rel="noopener noreffer">TUXEDO InfinityBook</a>, for my business and personal use, replacing my MacBook Pro M1 after using macOS for 15 years.</p>
<p>I am sharing the newest updates of the Quattro version, which onboarded a whole lot more people. One would say it went a little viral. It&rsquo;s everywhere on X and on socials. This is my take after using it for a month, comparing it to the previous versions, and showing what I like most, and maybe some things you should still be aware of.</p>
<blockquote>
<p>[!info]<br>
See more in <a href="/blog/macbook-to-arch-linux-omarchy/" rel="">Part 1</a> about my transition, what tools I needed, and in <a href="/blog/linux-omarchy-the-good-bad-and-fixable/" rel="">Part 2</a>, the good, the bad, and the fixable. I&rsquo;m also the original author of the Windows VM work (<a href="https://github.com/omacom/omarchy/pull/1333" target="_blank" rel="noopener noreffer">PR #1333</a>, merged via #2462), making Excel and Word available for me as I needed to write offers in Word and my daughters needed it for school. Recently I have also contributed a couple of plugins, the most used being the timezone plugin.</p>
</blockquote>
<h2 id="so-whats-new-in-quattro">So What&rsquo;s New in Quattro?</h2>
<p>Maybe the biggest question: what is new that started the <a href="https://omarchy.org/news/2026/09/quattro-crosses-200000-iso-downloads/" target="_blank" rel="noopener noreffer">hype</a> even more? Why is it all over social media when it was already out there for a full year?</p>
<p>The main reason is its <strong>integrated design</strong> with <a href="https://quickshell.org/" target="_blank" rel="noopener noreffer">Quickshell</a> (written in C++, Qt, QtQuick), which made the full interface one cohesive OS that is ultra fast<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It feels like an OS such as macOS, not like 100 different tools patched together, which is what Linux usually is. Sure, most distros have a cohesive look, but as soon as you want to change anything, you need to look at different dotfiles, different ways of configuring, some use toml, some json, others .ini files, etc. Omarchy built it all on the same tool chain, which is well chosen, ultra fast, and looks beautiful, and the config is unified with Hyprland bindings, monitors and toggles in Lua and the shell itself with <a href="https://doc.qt.io/qt-6/qmlreference.html" target="_blank" rel="noopener noreffer">QML</a>. If you don&rsquo;t know the details, everything is available through the integrated Omarchy Menu.</p>
<p>![[omarchy-menu.png]]</p>
<p>And even more so, if you want to change monitor settings, you can just do it from the top bars, and Omarchy will change the settings in your dotfiles. It&rsquo;s still the same tools underneath (most of them, as Omarchy comes with lots of great tools that are designed for Omarchy, e.g. <a href="https://github.com/tobi/omasnap" target="_blank" rel="noopener noreffer">Omasnap</a>, the best screen capture tool, which finally replaced my beloved Snagit adequately on Linux - also because I added the cut-out feature myself and scrolling capture was recently added too, omacut for video editing, omawrite, and a bunch of other tools). But otherwise it&rsquo;s the best-of-breed Linux utils and most importantly, <strong>[[TUIs]]</strong>.</p>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/omasnap-scrolling-obsidian-nvim.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">Omasnap, replaced beloved Snagit Screencasting with scrollable capture and horizontal and vertical in-line cut out</figcaption>
</figure>
<blockquote>
<p>[!note] Unified Menus, Settings, Launcher<br>
The new launcher is amazing in two distinct ways. Opening it and typing is so fast. It feels instant. With the old [[Walker Launcher]], I always had to physically wait before I typed what I searched for, as otherwise it would swallow the first letter.</p>
<p>Second, something I&rsquo;ve <a href="https://github.com/sspaeti/dotfiles/blob/4fa79fdb99da770185f60af2637f1485ba6ab3df/hypr/.config/hypr/sspaeti/omarchy-menu-flat" target="_blank" rel="noopener noreffer">built for myself</a> from day one: you can fuzzy search the config entries below the tree. E.g. if you want to change the background, you don&rsquo;t need to remember or know that it is under the parent <code>Style</code> and search for it for a couple of seconds, going into the wrong branches a couple of times. Instead, you just search <code>back..+ enter</code> and you&rsquo;re there.</p>
</blockquote>
<h3 id="pluggable-change-your-operating-system-the-malleable-os">Pluggable: Change Your Operating System, the Malleable OS</h3>
<p>But probably the biggest release and unlock for most people coming new into Omarchy (and Linux: remember, the target audience of Omarchy is not existing Linux users, who already know most of the glory and sheer beauty of tools out there, but macOS and Windows users) is the <strong><a href="https://plugins.omarchy.org/" target="_blank" rel="noopener noreffer">plugin marketplace</a></strong> (<a href="https://x.com/chyldm5d/status/2097614697046708414" target="_blank" rel="noopener noreffer">3000 in the less than a month</a>, currently at <code>3548</code>).</p>
<p>But why is this such a big thing, you might ask? Because it opens up the genie (Fable or GPT-6 Astra) to basically add any tool or visual to your OS that you want. Yeah, does not sound impressive, right? But ask Microsoft or Apple to add a timezone plugin into the actual OS, or change the ugly (personal taste) liquid glass back to the standard view, so it does not take <a href="https://x.com/abasisgenius/status/1997973419779096975" target="_blank" rel="noopener noreffer">drain battery</a> out of your laptop for making the windows translucent. You can&rsquo;t. You just get what you get. And the last couple of years, or months, were a disaster for macOS and Windows: instead of adding useful features, they shipped surveillance (hello <a href="https://youtu.be/PKmr2jF26sc" target="_blank" rel="noopener noreffer">Windows Recall</a>), or bad <a href="https://x.com/rossmllr/status/1987255842693922923" target="_blank" rel="noopener noreffer">rounded corners</a> on macOS, or bad liquid glass design choices you had no control over.</p>
<p>If you experience it once: one prompt to change this, or to integrate your personal API into your OS, and it changes your perspective. That&rsquo;s why people, especially non-technical ones who didn&rsquo;t even have the possibility to program an app before, can now just ask to build an alert, or add an OCR function to your saved clipboard images (as a <a href="https://plugins.omarchy.org/plugin.html?id=sspaeti.clipboard" target="_blank" rel="noopener noreffer">Plugin</a>, <a href="https://github.com/omacom/omarchy/pull/7452" target="_blank" rel="noopener noreffer">PR</a> to add to Omarchy), or update the fuzzy finder to search files, etc. I have literally changed 100s of things in my Omarchy setup. It might look similar to the default, but it&rsquo;s very customized to my workflow. Which is what I love most.</p>






<figure><video
  controls
  width="1124"
  height="568"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/omarchy-screen-theme-changes.mp4" type="">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">Custom made themes, based on my own pictures, as Omarchy theme.</figcaption>
</figure>
<p>I can customize the OS to work for me, instead of me needing to adapt to how the OS is working. Again, very hard to put into words if you haven&rsquo;t experienced it. That&rsquo;s why I recommend to everyone who is a little curious: take an old laptop that can&rsquo;t work anymore with the latest macOS or Windows, and just install Omarchy (needs a USB stick), it literally takes less than a minute (current world record, not released yet, <a href="https://x.com/dhh/status/2100916741429711312" target="_blank" rel="noopener noreffer">9 seconds</a>).</p>
<h2 id="its-not-a-better-macos-or-windows">It&rsquo;s not a Better macOS or Windows</h2>
<p>Also, DHH, the creator of Omarchy, didn&rsquo;t intend to make a better macOS or Windows. Instead, he created a whole new OS that didn&rsquo;t exist yet.</p>
<p>People who said it&rsquo;s just a couple of dotfiles are totally wrong. Because when I initially tried Linux, the sheer amount of work to make external monitors, internal camera, fingerprint, etc. work was astronomical. When I installed Omarchy the first time, it all just worked, out of the box. Most of my favorite tools were installed already, and design and theming are top notch.</p>
<p>Even more, it has a tiling manager, one thing I had in my macOS setup anyway, and it&rsquo;s very terminal and TUI centric, another thing I was <a href="https://www.youtube.com/watch?v=sStKFOwNaSM" target="_blank" rel="noopener noreffer">already doing</a> on macOS, so the switch was super natural to me. But I understand that for a normal Windows or macOS user, moving windows around, maximizing and closing windows with the mouse, this new OS might be overwhelming at first.</p>
<p>This OS is not meant to be learnt in minutes, it takes some effort to learn, and that&rsquo;s intentional. Because all good things, ways of working that are different from what we are used to, need some work. The best thing I can think of, as a [[Vim Language (and Motions)|Vim Motions]] lover, is Vim. You don&rsquo;t learn the commands and way of working in a day, you need at least a week to be roughly as proficient as with your old way. For full proficiency, it will take at least 3 months.</p>
<p>But they make it easier and easier to learn. There&rsquo;s a shortcut to just show all key shortcuts, or you can just pop up Learn Omarchy.</p>
<h2 id="what-else-the-agentic-os">What Else? The Agentic OS?</h2>
<p>I think that&rsquo;s it, these are the most critical changes compared to the version before. But wait, there&rsquo;s one more. The <strong><a href="https://world.hey.com/dhh/the-malleable-computer-7c187a9b" target="_blank" rel="noopener noreffer">malleable computer</a></strong>, as DHH likes to say. It integrates with any agent of choice, and they&rsquo;re all installable with &ldquo;one menu item&rdquo;. Every harness or model you can think of is there. And why is that? Because you can ask your agents to fix bugs with your monitor, or to set things up when you are at a presentation or a new co-working place. Before you might have needed to google and spend lots of time, now you are just one prompt away from almost anything.</p>
<p>This is what Linux could do before Omarchy, but not macOS or Windows. And it&rsquo;s now accessible, packaged in a single under-1min installer, beautifully designed and with all the features you want anyway, built by a large community.</p>
<p>This is what I haven&rsquo;t seen before. That&rsquo;s why there are so many people showing up, building things I needed anyway, and then sharing theirs with others. It&rsquo;s so infectious. That&rsquo;s why people who don&rsquo;t use it can&rsquo;t believe it. And the pace at which changes come in is so rapid that it&rsquo;s hard to keep up, and not to have an outdated opinion about it. Because the Omarchy I wrote about in <a href="/blog/macbook-to-arch-linux-omarchy/" rel="">Part 1</a>, or in <a href="/blog/linux-omarchy-the-good-bad-and-fixable/" rel="">Part 2</a>, is not the Omarchy Quattro of today anymore. And by the time I share this article, or you read this, I&rsquo;m sure there are 100 other new cool improvements worth sharing.</p>
<p>That&rsquo;s why you should keep an eye on the <a href="https://omarchy.org/news/rss.xml" target="_blank" rel="noopener noreffer">RSS feed</a> or <a href="https://omarchy.org/news/" target="_blank" rel="noopener noreffer">website news</a> to see what&rsquo;s going on. Also DHH has already hired through the <a href="https://omarchy.org/foundation/" target="_blank" rel="noopener noreffer">Omacom Foundation</a> a bunch of Linux folks who work on <a href="https://omarchy.org/news/2026/08/omacom-foundation-to-be-exclusive-hyprland-sponsor/" target="_blank" rel="noopener noreffer">Hyprland</a>, <a href="https://omarchy.org/news/2026/09/omacom-foundation-hires-outfoxxed/" target="_blank" rel="noopener noreffer">QuickShell</a> and <a href="https://omarchy.org/news/2026/09/omacom-foundation-hires-emir-beganovic/" target="_blank" rel="noopener noreffer">more</a>. People who otherwise had to do that in their free time can now work on it full time, hopefully with a comfortable salary, making the <strong>Linux experience better for everyone</strong>.</p>
<h3 id="improving-the-linux-experience-for-everyone-open-source-based">Improving the Linux Experience for Everyone, Open-Source Based</h3>
<p>This is what I fell in love with throughout my career anyway: open source software, sharing what you build for anyone to see your code, forking or contributing. Working on a common goal together, instead of each one in their private repos.</p>
<p>This is what Omarchy is: an open Linux repo where all the fixes each of us would <strong>otherwise have needed to suffer through</strong> now get shared, and even packaged in a way that we can install in a crazy 9 seconds!!!!</p>
<p>That&rsquo;s why everyone is so excited, at least the ones who realise this, or don&rsquo;t have countless hours to set up a hard disk, or a partition. At least, that&rsquo;s what I did in my early years, 100 times with Windows, and also a few times with Linux, and it was always a pain. Being afraid to hit the wrong partition, to make it too small and then later needing to repartition or reinstall the OS because I didn&rsquo;t choose a good default. These are things I don&rsquo;t enjoy.</p>
<h3 id="making-it-my-own-fonts-workflow-optimizations">Making it My Own: Fonts, Workflow Optimizations</h3>
<p>But what I enjoy are <strong>aesthetics and workflow improvements</strong>. I want to change the fonts in my terminal, I want to change my workflow for taking notes, or capturing print screens that have worked for me for the last 20 years, that I have optimized so diligently. So now there&rsquo;s an OS where I can take that to the moon, and I did.</p>
<p>Sure, there&rsquo;s a lot of time &ldquo;wasted&rdquo;, but I&rsquo;d say I loved it, I learned a ton, and why wouldn&rsquo;t I spend a ton of time optimizing the toolchain I use every day for my work? I&rsquo;m an author and data engineer who works 8-10 hours every day on the computer, sometimes even in my free time playing around with nerdy stuff like neovim, Obsidian and distraction-free typewriters. I love this. But every second I optimize is joy given back when I use the operating system. It&rsquo;s how I interface with the computer.</p>
<p>Omarchy has made it possible to take an awesome starting point, customize it to my needs, make it my own, while still getting all the new cool features that I might not have known I needed, but once there, I can just use them. E.g. just one example, the speech-to-text integration with voxtype is just so cool and handy, I&rsquo;m not a talker to my computer, but here and there I might use it. So I would not spend hours to set it up, but you know what, I once accidentally saw it on Twitter, I tried it, and it just worked.</p>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/voxtype-omarchy.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">This is me just speaking and see how fast the real time translation is. That came just out of the box, I didn&rsquo;t setup anything. These are the kind of feature you&rsquo;d expect from an OS update.</figcaption>
</figure>
<p>This is really the beauty of Omarchy, for me, and why Quattro took off so much. And potentially for you too? But we are just at the start, so there&rsquo;s much more to come, and I&rsquo;m very excited for it. Though I also just value using the system, I need to get work done. So even if you don&rsquo;t want all the hype or changes, you can just use Omarchy, and you get the best Linux experience out there. The latest update included a bespoke <a href="https://github.com/omacom/omarchy/releases/tag/v4.0.4" target="_blank" rel="noopener noreffer">Omarchy Linux Kernel</a>, just for Omarchy, to keep the desktop responsive and multitasking smooth, even under heavy load or memory pressure.</p>
<p>So that&rsquo;s a lot of words for the 4.0 version of a Linux distro, but I hope I could make it clearer what it is all about.</p>
<p>If you want to know more about <a href="https://www.ssp.sh/brain/my-terminal-workflow-with-macos-and-linux/" target="_blank" rel="noopener noreffer">my terminal workflows,</a> (note-taking, database, writing with Zen and Nvim) or nerdy Omarchy-related stuff, check out my second brain note on [[Omarchy]] with all its backlinks and graph, where you can see related notes. Especially check out the beautiful [[TUIs]], this is really my favorite places to be in when on a computer. For example, for this article is written on a [[My Distraction-Free Typewriter (Micro Journal)|Typewriter]], no GUI, just plain terminal style (Simple Raspberry Pi Zero 2 with Debian and [[Neovim]]).</p>













  

























<figure><span class="img-expandable" data-full="/blog/omarchy-linux-quattro/microjournal.jpg">
<a target="_blank" href="/blog/omarchy-linux-quattro/microjournal.jpg" title="Typed on my beloved Distraction-Free Typewriter (Micro Journal)">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Typed on my beloved <a href="https://www.ssp.sh/brain/my-distraction-free-typewriter-micro-journal/" target="_blank" rel="noopener noreffer">Distraction-Free Typewriter (Micro Journal)</a></figcaption>
</figure>
<p>Find more related links and the custom plugins I&rsquo;ve contributed to Omarchy so far. Thanks for reading this far. Feel free to share your workflow and how you use Omarchy—I&rsquo;m always interested in learning more.</p>
<h2 id="further-reads">Further Reads</h2>
<ul>
<li>[[Omarchy|Omarchy Note]] with backlinks and interactive graph to find related notes.</li>
<li><a href="/blog/macbook-to-arch-linux-omarchy/" rel="">Part 1: My Journey from macOS to Arch Linux with Omarchy</a></li>
<li><a href="/blog/linux-omarchy-the-good-bad-and-fixable/" rel="">Part 2: Arch Linux (Omarchy) — 8 Months Later: The Good, the Bad, and the Fixable</a></li>
<li><a href="https://youtu.be/XOp8lngtmPg?si=_BUkQLEwBtEd4lC3" target="_blank" rel="noopener noreffer">My Omarchy workflow video</a> and <a href="https://www.youtube.com/watch?v=sStKFOwNaSM" target="_blank" rel="noopener noreffer">macOS for comparison</a></li>
<li><a href="https://luma.com/xmkodjwv" target="_blank" rel="noopener noreffer">Omarchy Event Switzerland (Biel P15)</a></li>
<li><a href="https://github.com/sspaeti/dotfiles/" target="_blank" rel="noopener noreffer">All my dotfiles</a>, including omarchy under Linux mostly under <code>linux_applications</code>, <code>omarchy</code>, <code>hypr</code></li>
</ul>
<h3 id="my-plugins">My Plugins</h3>
<p>All <a href="https://plugins.omarchy.org/?author=sspaeti" target="_blank" rel="noopener noreffer">my Omarchy plugins</a>:<br>
![[sspaeti-omarchy-plugins.png]]</p>
<h4 id="timezones">Timezones</h4>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/timezone-plugin.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">Timezone widget to see what time it is in my client&rsquo;s timezone</figcaption>
</figure>
<h4 id="search-images-with-ocr-from-clipboard">Search Images with OCR from Clipboard</h4>













  

























<figure><span class="img-expandable" data-full="/blog/omarchy-linux-quattro/oct-clipboard.png">
<a target="_blank" href="/blog/omarchy-linux-quattro/oct-clipboard.png" title="Plugin and change of Omarchy to text-search my clipboard images">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Plugin and change of Omarchy to text-search my clipboard images</figcaption>
</figure>
<h5 id="and-this-is-how-i-search-my-files">And this is how I search my files</h5>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/omarchy-file-content-search.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">How I search files on my machine</figcaption>
</figure>
<h4 id="tui-email-client-in-the-top-bar">TUI Email Client in the top bar</h4>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/omarchy-neomd-plugin.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">Integrated email for Omarchy with my email client Neomd</figcaption>
</figure>
<h4 id="my-website-analytics-in-the-top-bar">My Website Analytics in the top bar</h4>






<figure><video
  controls
  width="1280"
  height="720"
  style="max-width: 100%; height: auto;"
  autoplay
  loop
  muted
  
  
>
  <source src="/blog/omarchy-linux-quattro/preview-goatcounter-omarchy-plugin.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video><figcaption class="image-caption">Goatcounter, my website analytics, integrated into my Operating system</figcaption>
</figure>
<h4 id="cliamp-listening-music-in-a-tui">Cliamp: Listening Music in a TUI</h4>
<p>Not my plugin, but one I tested, and super cool to listen Spotify:<br>


<div class="x-embed-wrapper" style="display: flex; justify-content: center; margin: 1.5em 0;">
          <blockquote class="twitter-tweet" data-dnt="true"><p lang="en" dir="ltr">Need a better Spotify client that looks cool and uses 2GB less memory? <br><br>Try Cliamp, inspired by Winamp. Beautiful little TUI. <a href="https://t.co/l5MWBxhAnk">https://t.co/l5MWBxhAnk</a> <a href="https://t.co/2Nk6HDyBY6">pic.twitter.com/2Nk6HDyBY6</a></p>&mdash; Simon Späti 🏔️ (@sspaeti) <a href="https://x.com/sspaeti/status/2088903240175722719?ref_src=twsrc%5Etfw">August 16, 2026</a></blockquote>
<script async src="https://platform.x.com/widgets.js" charset="utf-8"></script>


        </div>
        <script>
        (function() {
          function updateXTheme() {
            var isDark = document.body.getAttribute('theme') === 'dark';
            var theme = isDark ? 'dark' : 'light';
            
            document.querySelectorAll('blockquote.twitter-tweet').forEach(function(el) {
              el.setAttribute('data-theme', theme);
            });
            
            document.querySelectorAll('.x-embed-wrapper iframe').forEach(function(iframe) {
              var src = iframe.src;
              if (src) {
                var url = new URL(src);
                if (url.searchParams.get('theme') !== theme) {
                  url.searchParams.set('theme', theme);
                  iframe.src = url.toString();
                }
              }
            });
          }
          
          updateXTheme();
          
          new MutationObserver(function(mutations) {
            mutations.forEach(function(m) {
              if (m.attributeName === 'theme') updateXTheme();
            });
          }).observe(document.body, { attributes: true });
        })();
        </script></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Quickshell is newly used for the <em>bar, launcher, menus, notifications, on-screen displays, control panels, lock screen, and polkit agent</em> now all live inside a single long-running shell process with a <strong>plugin architecture</strong>. That means <em>Waybar, Walker, Mako, SwayOSD, hyprlock, hypridle, swaybg, and polkit-gnome</em> are all gone, replaced by one coherent, fully-themed, IPC-scriptable shell. <a href="https://github.com/omacom/omarchy/releases/tag/v4.0.0" target="_blank" rel="noopener noreffer">Release notes</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>Obsidian Vault to Enterprise Company Brain: Where Agents Write, Branch and Merge</title>
    <link>https://www.ssp.sh/blog/from-obsidian-to-enterprise-company-brain/</link>
    <pubDate>Tue, 15 Sep 2026 08:00:08 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/from-obsidian-to-enterprise-company-brain/</guid><enclosure url="https://www.ssp.sh/blog/from-obsidian-to-enterprise-company-brain/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>I&rsquo;ve been using a personal second brain for many years. I started with OneNote and pivoted to Obsidian due to the linking feature. Since then, I have built a full net of knowledge for my personal life, but not only that: I also manage my solo business with Obsidian, such as my CRM or everything knowledge-related to my company.</p>
<p>But what if you want the same approach for a bigger company, enterprise scale with lots of information internally, but also available externally? How do you manage knowledge in that context, with multiple writers, lots of consumers, and always up to date? Though enterprise knowledge management isn&rsquo;t something new, a self-curating LLM Wiki that Andrej Karpathy shared in a short <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f" target="_blank" rel="noopener noreffer">gist</a> went viral, and it showed what&rsquo;s possible for a small personal, auto-updated knowledge wiki. But it&rsquo;s not working at enterprise scale, and also, there&rsquo;s no verification process or versioning happening.</p>
<p>In this article, we go from a personal brain to an enterprise company brain and show the feature differences, what enables them, and how agents help us not only read context but also write and update information. We explore how agents write on their own branches (with changes that don&rsquo;t collide and with collisions where humans review and decide). Everything is accompanied by two showcases at the end that compare the two brains and their scale.</p>
<h2 id="how-it-started-enterprise-knowledge-management">How it Started: Enterprise Knowledge Management</h2>
<p>If we look at the history of six decades of knowledge management, we see that it&rsquo;s not something entirely new, but the way we work, from foundational to wikis, over to graphs and now LLM-enhanced, is interesting:</p>
<div class="mermaid-container">
    <div class="mermaid" id="id-4"></div><button type="button" class="expand-btn" aria-label="Expand diagram" title="Expand diagram" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</div><!-- 












  

























<figure><span class="img-expandable" data-full="/blog/from-obsidian-to-enterprise-company-brain/mermaid-knowledge-management-evolution.png">
<a target="_blank" href="/blog/from-obsidian-to-enterprise-company-brain/mermaid-knowledge-management-evolution.png" title="Enterprise &amp; Personal Knowledge Management Evolution">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Enterprise &amp; Personal Knowledge Management Evolution</figcaption>
</figure> -->
<p>For my personal knowledge brain, I use a graph to connect my personal notes for my personal life, school, and even work. It started with Roam, but you can use any linked note-taking app such as Obsidian or Logseq. I&rsquo;d suggest using an open file format like <strong>Markdown plaintext files</strong>, so your knowledge is not tied to the tool.</p>
<p>If we assess what a single-user connected graph system looks like, we can then compare its limitations and how it compares to enterprise knowledge management. A personal system stands for:</p>
<ul>
<li>A place to <strong>capture and collect</strong> &ldquo;knowledge&rdquo; for long-term retention (an open format that still works in 100 years)</li>
<li><strong>Idea creation</strong>, a place where we can <strong>curate and write</strong> knowledge</li>
<li><strong>Understanding and learning</strong> new things through interlinking notes (building the graph below)</li>
<li>Knowledge retrieval and getting insights from connections that have been written over years</li>
<li>Templates to automate common note structures, integration with knowledge capturing tools (e.g. <a href="https://obsidian.md/clipper" target="_blank" rel="noopener noreffer">Obsidian Web Clipper</a>)</li>
</ul>
<p>There are probably many more, but these are the essence of what a personal system should provide you.</p>













  
<figure><span class="img-expandable" data-full="/blog/from-obsidian-to-enterprise-company-brain/obsidian-graph.webp">
<a target="_blank" href="/blog/from-obsidian-to-enterprise-company-brain/obsidian-graph.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Interconnected graph of personal knowledge, and how your system looks in graph form. | More at  <a href="https://ssp.sh/brain/todays-daily-graphs-obsidian-graph/" target="_blank" rel="noopener noreffer">Todays Daily Graphs</a></figcaption>
</figure>
<h3 id="limitations-of-a-personal-knowledge-brain">Limitations of a Personal Knowledge Brain</h3>
<p>There are real practical limitations and disadvantages a personal brain has over a company and enterprise brain. There are <strong>no multi-user capabilities</strong>, or hardly any, with just local Markdown files (you&rsquo;d need to sync them with <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" target="_blank" rel="noopener noreffer">CRDTs</a> or find another way), but ultimately, text files or Markdown files are not made for it.</p>
<p>The graph also has no types or way to guarantee that a data type is persistent. It&rsquo;s unstructured, &ldquo;free-flow&rdquo; text. Linked notes (mostly <a href="https://en.wikipedia.org/wiki/Zettelkasten" target="_blank" rel="noopener noreffer">Zettelkasten</a>) are provided with limited semantics between the notes and the relations between them. This is what Sergei Simonovi <a href="https://www.lesswrong.com/posts/DtpjeBf9bzzqNJDmr/rethinking-the-second-brain-from-storing-information-to-1" target="_blank" rel="noopener noreffer">says as well</a>, noting that this can lead to &ldquo;friction points when working with a large number of notes&rdquo;.</p>
<p>If we have not 1000s, but millions of notes, we need an index for fast retrieval, we might need embeddings/clustering, and we need something that scales with the company size and all types of data (TXT, CSV, APIs, JSON, etc.).</p>
<h2 id="enterprise-knowledge-management-scale-multiple-users-concurrent-writes-auto-update">Enterprise Knowledge Management Scale: Multiple Users, Concurrent Writes, Auto Update</h2>
<p>Surprisingly maybe, all of the requirements we have for a personal brain, we also have for a business. We need to <strong>retrieve last month&rsquo;s/year&rsquo;s sales numbers</strong>, we <strong>want to learn new insights</strong> from an ever-changing business environment (new laws, new provided data we can use), and we <strong>want to share with internal and external stakeholders</strong>.</p>
<p>But it&rsquo;s on a much bigger scale. We need to work with multiple users and bigger, more sophisticated data sets. We can&rsquo;t just copy-paste from a website. We need data pipelines, a data warehouse.</p>
<p>We want automation, as well as governance. In a sense, you can <strong>imagine replacing some custom software products</strong> like CRMs or ERPs that need dedicated systems and SW, especially CRMs, which gather all sorts of context, from customers combined with inputs from the sales reps on when they called last, what the customer said, what their temperature is right now (hot/warm/cold), etc. If that can be gathered from notes of sales reps automatically, or from dedicated logs from the phone if approved, or similar, these states can be managed in a company brain, and it is a good example of what we need.</p>
<h3 id="features-of-such-an-enterprise-company-brain">Features of such an Enterprise Company Brain</h3>
<p>An enterprise company&rsquo;s brain needs multiple users to write to it concurrently, and we need automation. This is where agents join the picture, and agents play a big part, but not the only one. Being able to version state when multiple concurrent users write is key, or you need a storage system that can handle all requests and the volume.</p>
<h4 id="agents-first-governance-for-auto-update-context">Agents-First Governance for Auto-update Context</h4>
<p>Agents are very good at <strong>capturing information</strong> in any format. Agents should give us the comfort of doing that autonomously. We want them to read defined locations with information that can be valid for a company brain. Staying with the CRM example, all customers from the database should be automatically updated every day, calls and docs should be ingested and updated, deals and contracts should be related and attached to each customer, and so on.</p>
<p>But how do they write new context to the company brain?</p>
<h4 id="versioned-with-branches">Versioned: with Branches</h4>
<p>That&rsquo;s where we need branching. Not only because 100s of AI agents adding knowledge at once will trip over each other, but also because every change can be <strong>reviewed before it lands</strong>. Without that, a company brain fed by agents drifts into noise. Auto-changing a wrong phone number, or a hallucinated deal status, can lead to an untrustworthy state in a few weeks.</p>
<p>So we give each agent its own branch and its own private copy. It writes there, and then its changes get merged back into the <code>main</code> branch, exactly like a GitHub pull request. Nothing lands on the &ldquo;real&rdquo; version until it&rsquo;s approved. <strong>Humans can review and decline certain merges</strong>, or revert them later, and you get a full history of who changed what.</p>
<p>On top of that, agents and humans can work at the same time without corrupting anything. This avoids conflicts and lets multiple agents work in parallel. Branches are also great for <strong>long-running tasks</strong>, since they can run concurrently and merge back when they&rsquo;re done. So you get <strong>verification</strong> and multi-concurrency in one go.</p>
<p>More generally, a <strong>requirement for a useful company brain</strong> is that it stays continuously up to date, correct, and enriched (see CRMs that are always stale, missing, or incorrect, so humans patch by asking colleagues, but agents act directly). Branches are what make &ldquo;continuously updated&rdquo; and &ldquo;still correct&rdquo; work together.</p>
<h4 id="cheap-storage-accessible-for-everyone-object-storage">Cheap Storage, Accessible for Everyone: Object Storage</h4>
<p>We need an open space where data can be written, where we can version our data. The best place, one that is affordable in price, is object storage. Besides its easy setup and ease of use, it also allows using open formats such as Apache Parquet or other [[Data Lake File Formats|open file formats]] (you can also use <a href="https://www.startdataengineering.com/post/what_why_table_format/" target="_blank" rel="noopener noreffer">open table formats</a> if you like, with added database features on top of files) in a place for anyone to read the information with the right access, unlike piping the data into a proprietary database format.</p>
<p>There&rsquo;s no database server to run either: your whole graph is a bunch of files.</p>
<h4 id="consistency-with-agents-typed-graph">Consistency with Agents: Typed Graph</h4>
<p>So how do we make sure that agents write the information grepped from various sources in a consistent way? Unlike the Obsidian Markdown example, where we link them with <code>[ [wikilinks] ]</code> and data is stored free-flow, we can use a typed graph. Meaning the data is stored in a graph linked like Obsidian, but typed with a datatype. So the system you use makes sure that the agents consistently write a number for a client&rsquo;s phone number, or that dates are aligned, etc.</p>
<p>These types act as <a href="https://datacontract.com/" target="_blank" rel="noopener noreffer">data contracts</a> for agents on how to store, but also how to retrieve the right information. It&rsquo;s also key for agents to avoid writing bad data. Instead of separate tables, a typed graph stores everything as a graph (dots and lines). <em>Dots</em> are things (a person, a company, a deal). <em>Lines</em> are how they connect (this person founded that company). It&rsquo;s basically the <strong>&ldquo;connections&rdquo; view in Obsidian</strong>, or how a social network knows who&rsquo;s friends with who.</p>
<p><strong>&ldquo;Typed&rdquo; means it has rules</strong>. Every dot and line has a defined shape you write down first (a Person must have a name, and a FoundedBy line only connects a person to a company). So nothing unrelated can sneak in. Think of it like a form with required fields vs. a blank notebook where anyone writes anything.</p>
<p>We can store <strong>graphs as code</strong>. Here&rsquo;s an example of the node <code>Organization</code>:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">node Organization {
</span></span><span class="line"><span class="cl">    slug: String @key
</span></span><span class="line"><span class="cl">    name: String @index
</span></span><span class="line"><span class="cl">    brief: String?
</span></span><span class="line"><span class="cl">    description: String?
</span></span><span class="line"><span class="cl">    website: String?
</span></span><span class="line"><span class="cl">    founded_year: I32?
</span></span><span class="line"><span class="cl">    hq: String?
</span></span><span class="line"><span class="cl">...</span></span></code></pre></td></tr></table>
</div></div></div>
<p>Explained in Obsidian terms: a graph link is a connection you declared (&ldquo;A links to B&rdquo;), while an embedding is a resemblance the model inferred (&ldquo;A reads like B&rdquo;). A company brain that holds both can retrieve on meaning and on typed relationships, not just on vector similarity alone.</p>
<p>Together with branches that are versioned, being able to revert certain changes or decline them altogether, built on an open and standard storage layer such as S3, R2, or others, we can increase the quality and consistency a lot.</p>
<p>This is something we&rsquo;ve tried to keep in check with Master Data Management (MDM), where we used Excel for <a href="https://en.wikipedia.org/wiki/Data_steward" target="_blank" rel="noopener noreffer">stewardship</a> of data, but human-powered, which was always the bottleneck: domain experts were already the busiest, and now they get one more job, to check if the data was correct and approve it. With agents, we can accelerate this process, if we have quality documentation and up-to-date files the models can verify against.</p>
<p>Also, there&rsquo;s no absolute truth, and agents can also surface contradictions that naturally happen in the data. Maybe one piece of documentation says the last call for a client was last month, while a log entry shows something else. In such a system, the human can always intercept and change the data too, same as the agents would.</p>
<p>It&rsquo;s a <strong>continuously shared understanding</strong> that is developed together with the agents and the whole company.</p>
<h3 id="do-we-need-ontologies">Do We Need Ontologies?</h3>
<p>But with graphs, we can model data with more accuracy and more detail. This is one advantage. We can model world knowledge into ontologies, and combine them with the enterprise data, which helps the model get more domain understanding with the right &ldquo;connected&rdquo; world+local and business knowledge.</p>
<p>The graph is the most generalized version of an ontology, Ragnor <a href="https://x.com/kozlovski/status/2066182400002990216" target="_blank" rel="noopener noreffer">says</a>. Text links linearly to other texts. A graph has many categories. Agents need an understanding of the domain (what&rsquo;s a customer -&gt; connection to data model etc.). An <strong>ontology is a world of objects that connect together</strong>, and the graph that connects it all, with indexes, embeddings, and the open storage format.</p>
<p>These ontologies are especially helpful for company brains, as they help find a common denominator across different departments. Personal Obsidian knowledge brains don&rsquo;t need them, but they can still be helpful if you want to add the complexity.</p>
<p>A graph can be represented as a table, JSON, etc., but a table can&rsquo;t be converted back into a graph, so you still get the tabular format if you need it. A16z <a href="https://a16z.com/your-data-agents-need-context/" target="_blank" rel="noopener noreffer">argues</a> that the reason using agents hasn&rsquo;t worked so far was &ldquo;<strong>a lack of proper data context</strong>&rdquo;, and that &ldquo;Enterprise data today is still incredibly disparate and messy&rdquo;, which is why data agents struggle to answer basic questions.</p>
<p>And why not use a relational database? Andrew <a href="https://x.com/kozlovski/status/2066182400002990216" target="_blank" rel="noopener noreffer">reframes</a> the problem: Postgres is 40-something years old, and was made due to compute and other restrictions of its time. A design from first principles for agents that need lots of interconnected, real-world knowledge combined with our data is essentially a graph, not a tabular format. Combined with ontologies that describe the entities in the world, this can be extremely powerful for context.</p>
<h2 id="showcase-managing-crm-with-obsidian-and-with-multiple-agents">Showcase: Managing CRM with Obsidian, and with Multiple Agents</h2>
<p>Let&rsquo;s look at the personal brain versus the enterprise company brain.</p>













  

























<figure><span class="img-expandable" data-full="/blog/from-obsidian-to-enterprise-company-brain/personal-vs-company-brain.png">
<a target="_blank" href="/blog/from-obsidian-to-enterprise-company-brain/personal-vs-company-brain.png" title="From Personal to Company Brain">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">From Personal to Company Brain</figcaption>
</figure>
<h3 id="personal-brain-with-obsidian">Personal Brain with Obsidian</h3>
<p>The example of a CRM is chosen somewhat illustratively, as I have built and [[Managing My Business with Obsidian|manage my own CRM system within Obsidian]]. I use a note for each client, and then, with a dedicated #hashtag, bring them together in one table. I use the <a href="https://dev.to/dailydevtips1/what-exactly-is-frontmatter-123g" target="_blank" rel="noopener noreffer">Frontmatter</a> (metadata of a Markdown file in the header) to attribute each client: how active everyone is, and who I need to reach out to.</p>
<p>With different views such as <strong>Sales Funnel</strong> or <strong>overview</strong>, I can then see different things. Sales Funnel with <strong>Kanban View</strong> - based on the same data as the above table:</p>













  
<figure><span class="img-expandable" data-full="/blog/from-obsidian-to-enterprise-company-brain/obsidian-sales-funnel.webp">
<a target="_blank" href="/blog/from-obsidian-to-enterprise-company-brain/obsidian-sales-funnel.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">From Managing My Business with Obsidian</figcaption>
</figure>
<h3 id="company-brain-with-omnigraph">Company Brain with OmniGraph</h3>
<p>If we expand this to a company brain with <em>agents writing, everything versioned, stored in an open format</em>, here&rsquo;s the example with <a href="https://github.com/ModernRelay/omnigraph" target="_blank" rel="noopener noreffer">OmniGraph</a>: it uses <a href="https://github.com/lancedb/lance" target="_blank" rel="noopener noreffer">Lance</a> as the open columnar storage format on object storage (your graph is just files in a bucket you own, no database server), <a href="https://github.com/apache/datafusion" target="_blank" rel="noopener noreffer">DataFusion</a> as the query engine, and builds the typed graph with git-style branches on top.</p>
<p>I took my own Obsidian CRM and rebuilt it as a Company Brain at <a href="https://github.com/ssp-data/company-brain-crm" target="_blank" rel="noopener noreffer">company-brain-crm</a> (adapted from the <a href="https://github.com/ModernRelay/omnigraph-cookbooks/tree/main/vc-os" target="_blank" rel="noopener noreffer"><code>vc-os</code> cookbook example</a>), a company brain for my company, SSP Data, scaled up to a fictional 50-person version that does a lot of outreach for data-engineering articles and consulting.</p>
<p>Every field from my Obsidian frontmatter got a type added: my <code>Hotness: 5-Cold</code> becomes <code>hotness: enum(hot, warm, cold)</code> that the system enforces (an agent physically can&rsquo;t write &ldquo;5-Cold-ish&rdquo;), my <code>## Log</code> section becomes <code>Interaction</code> nodes (call / email / meeting / note), and my <code>#crmssp</code> tag becomes the <code>Company</code> node type itself.</p>
<p>The whole CRM is <strong>three files you commit to git</strong>: <a href="https://github.com/ssp-data/company-brain-crm/blob/main/schema.pg" target="_blank" rel="noopener noreffer"><code>schema.pg</code></a> (4 node types — Company, Person, Deal, Interaction — and 7 edge types):</p>
<div class="highlight"><div class="chroma"><div class="code-header language-js"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">node</span> <span class="nx">Company</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">slug</span><span class="o">:</span> <span class="nb">String</span> <span class="err">@</span><span class="nx">key</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="nb">String</span> <span class="err">@</span><span class="nx">index</span>
</span></span><span class="line"><span class="cl">    <span class="nx">kind</span><span class="o">:</span> <span class="kr">enum</span><span class="p">(</span><span class="nx">us</span><span class="p">,</span> <span class="nx">prospect</span><span class="p">,</span> <span class="nx">client</span><span class="p">,</span> <span class="nx">publisher</span><span class="p">,</span> <span class="nx">partner</span><span class="p">)</span> <span class="err">@</span><span class="nx">index</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// the old Obsidian frontmatter `Hotness: 5-Cold`, now enforced
</span></span></span><span class="line"><span class="cl">    <span class="nx">hotness</span><span class="o">:</span> <span class="kr">enum</span><span class="p">(</span><span class="nx">hot</span><span class="p">,</span> <span class="nx">warm</span><span class="p">,</span> <span class="nx">cold</span><span class="p">)</span><span class="o">?</span> <span class="err">@</span><span class="nx">index</span>
</span></span><span class="line"><span class="cl">    <span class="nx">segment</span><span class="o">:</span> <span class="nb">String</span><span class="o">?</span>
</span></span><span class="line"><span class="cl">    <span class="nx">notes</span><span class="o">:</span> <span class="nb">String</span><span class="o">?</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">node</span> <span class="nx">Deal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="nx">stage</span><span class="o">:</span> <span class="kr">enum</span><span class="p">(</span><span class="nx">lead</span><span class="p">,</span> <span class="nx">contacted</span><span class="p">,</span> <span class="nx">proposal</span><span class="p">,</span> <span class="nx">won</span><span class="p">,</span> <span class="nx">lost</span><span class="p">)</span> <span class="err">@</span><span class="nx">index</span>  <span class="c1">// the sales funnel
</span></span></span><span class="line"><span class="cl">    <span class="nx">value_usd</span><span class="o">:</span> <span class="nx">F64</span><span class="o">?</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// the [[wikilinks]], but with meaning
</span></span></span><span class="line"><span class="cl"><span class="nx">edge</span> <span class="nx">WorksAt</span><span class="o">:</span> <span class="nx">Person</span> <span class="o">-&gt;</span> <span class="nx">Company</span> <span class="p">{</span> <span class="nx">role</span><span class="o">:</span> <span class="nb">String</span><span class="o">?</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">edge</span> <span class="nx">Knows</span><span class="o">:</span> <span class="nx">Person</span> <span class="o">-&gt;</span> <span class="nx">Person</span> <span class="p">{</span> <span class="nx">context</span><span class="o">:</span> <span class="nb">String</span><span class="o">?</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">edge</span> <span class="nx">DealWith</span><span class="o">:</span> <span class="nx">Deal</span> <span class="o">-&gt;</span> <span class="nx">Company</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p><a href="https://github.com/ssp-data/company-brain-crm/blob/main/queries/crm.gq" target="_blank" rel="noopener noreffer"><code>queries/crm.gq</code></a> with the recurring questions, written as typed, lintable stored queries:</p>
<div class="highlight"><div class="chroma"><div class="code-header language-js"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// The sales funnel — the kanban board as a query.
</span></span></span><span class="line"><span class="cl"><span class="nx">query</span> <span class="nx">sales_funnel</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">match</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$d</span><span class="o">:</span> <span class="nx">Deal</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$d</span> <span class="nx">dealWith</span> <span class="nx">$c</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">stage</span><span class="p">,</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">$c</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">kind</span><span class="p">,</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">value_usd</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">order</span> <span class="p">{</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">stage</span><span class="p">,</span> <span class="nx">$d</span><span class="p">.</span><span class="nx">name</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Warm-intro path: which SSP teammate knows someone at the target company?
</span></span></span><span class="line"><span class="cl"><span class="nx">query</span> <span class="nx">warm_intro</span><span class="p">(</span><span class="nx">$slug</span><span class="o">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">match</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$target</span><span class="o">:</span> <span class="nx">Company</span> <span class="p">{</span> <span class="nx">slug</span><span class="o">:</span> <span class="nx">$slug</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$contact</span><span class="o">:</span> <span class="nx">Person</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$contact</span> <span class="nx">worksAt</span> <span class="nx">$target</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$teammate</span><span class="o">:</span> <span class="nx">Person</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$teammate</span> <span class="nx">knows</span> <span class="nx">$contact</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$teammate</span> <span class="nx">worksAt</span> <span class="nx">$ssp</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$ssp</span><span class="o">:</span> <span class="nx">Company</span> <span class="p">{</span> <span class="nx">slug</span><span class="o">:</span> <span class="s2">&#34;com-ssp&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span> <span class="nx">$teammate</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">$contact</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">$contact</span><span class="p">.</span><span class="nx">role</span><span class="p">,</span> <span class="nx">$target</span><span class="p">.</span><span class="nx">name</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>and <a href="https://github.com/ssp-data/company-brain-crm/blob/main/seed.jsonl" target="_blank" rel="noopener noreffer"><code>seed.jsonl</code></a> (the data — one JSON line per node or edge):</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-json"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;Company&#34;</span><span class="p">,</span> <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;com-nordbahn&#34;</span><span class="p">,</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Nordbahn Logistik&#34;</span><span class="p">,</span> <span class="nt">&#34;kind&#34;</span><span class="p">:</span> <span class="s2">&#34;prospect&#34;</span><span class="p">,</span> <span class="nt">&#34;hotness&#34;</span><span class="p">:</span> <span class="s2">&#34;warm&#34;</span><span class="p">,</span> <span class="nt">&#34;segment&#34;</span><span class="p">:</span> <span class="s2">&#34;logistics&#34;</span><span class="p">,</span> <span class="err">...</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;Person&#34;</span><span class="p">,</span> <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;per-tom-vermeer&#34;</span><span class="p">,</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Tom Vermeer&#34;</span><span class="p">,</span> <span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;Account Executive&#34;</span><span class="p">,</span> <span class="err">...</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;edge&#34;</span><span class="p">:</span> <span class="s2">&#34;Knows&#34;</span><span class="p">,</span> <span class="nt">&#34;from&#34;</span><span class="p">:</span> <span class="s2">&#34;per-tom-vermeer&#34;</span><span class="p">,</span> <span class="nt">&#34;to&#34;</span><span class="p">:</span> <span class="s2">&#34;per-lars-petersen&#34;</span><span class="p">,</span> <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;context&#34;</span><span class="p">:</span> <span class="s2">&#34;met at Data Council booth&#34;</span><span class="p">}}</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>Run it with <code>make init &amp;&amp; make seed</code>, and you have a <strong>98-node / 123-edge</strong> CRM, locally on your disk, no database needed. You can swap the path for an <code>s3://…</code> bucket and the exact same flow runs on object storage. From there, every view I had in Obsidian, such as the sales-funnel kanban or the follow-up table, is a <strong>stored query you invoke by name</strong> (<code>sales_funnel</code>, <code>hot_prospects</code>, <code>company_log</code>). Every result of this demo is captured in <a href="https://github.com/ssp-data/company-brain-crm/blob/main/DEMO.md" target="_blank" rel="noopener noreffer">DEMO.md</a>, such as <a href="https://github.com/ssp-data/company-brain-crm/blob/main/DEMO.md#make-funnel" target="_blank" rel="noopener noreffer">the sales funnel as a query result</a>, the same data as my Obsidian kanban above.</p>
<p>The <strong>context layer is versioned, diffable, and reproducible</strong> from a clean checkout, exactly like the rest of your infrastructure.</p>
<h4 id="how-do-agents-or-users-get-context-out">How Do Agents or Users Get Context Out?</h4>
<p>How does an agent (or a colleague) actually <em>get</em> context out of this? It&rsquo;s via calling a query. Take the question my Obsidian backlinks could never answer across 50 people&rsquo;s networks: &ldquo;who can warm-intro us at a target company?&rdquo;</p>
<p>The result is one traversal: <code>teammate → knows → contact → works</code> at target:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-bash"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ omnigraph query warm_intro --params <span class="s1">&#39;{&#34;slug&#34;:&#34;com-nordbahn&#34;}&#39;</span> ...
</span></span><span class="line"><span class="cl">teammate.name <span class="p">|</span> contact.name  <span class="p">|</span> contact.role <span class="p">|</span> target.name
</span></span><span class="line"><span class="cl">--------------+---------------+--------------+------------------
</span></span><span class="line"><span class="cl">Tom Vermeer   <span class="p">|</span> Lars Petersen <span class="p">|</span> Head of BI   <span class="p">|</span> Nordbahn Logistik
</span></span><span class="line"><span class="cl">...</span></span></code></pre></td></tr></table>
</div></div></div>
<h4 id="writing-back-to-the-crm">Writing back to the CRM</h4>













  

























<figure><span class="img-expandable" data-full="/blog/from-obsidian-to-enterprise-company-brain/agents-write-humans-merge.png">
<a target="_blank" href="/blog/from-obsidian-to-enterprise-company-brain/agents-write-humans-merge.png" title="How auto-merge and conflicts are handled: agents write on branches, humans decide on collisions">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">How auto-merge and conflicts are handled: agents write on branches, humans decide on collisions</figcaption>
</figure>
<p>And now the write-back side, which most context layers or agents can&rsquo;t do yet, but which OmniGraph supports. A CRM goes stale the day nobody updates it, so in this demo two agents keep it fresh, and neither writes to <code>main</code> directly. Commands from the repo you can try, such as <code>make agents</code>, run them:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-bash"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># the inbox agent logs this morning&#39;s replies on ITS OWN branch — main untouched</span>
</span></span><span class="line"><span class="cl">omnigraph load --data agents/inbox-scan.jsonl --mode merge <span class="se">\
</span></span></span><span class="line"><span class="cl">  --branch agent/inbox-scan --from main --as agent-inbox --store graphs/crm.omni
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># meanwhile the billing agent marks a signed PoC as won — on a second branch</span>
</span></span><span class="line"><span class="cl">omnigraph load --data agents/billing-sync.jsonl --mode merge <span class="se">\
</span></span></span><span class="line"><span class="cl">  --branch agent/billing-sync --from main --as agent-billing --store graphs/crm.omni</span></span></code></pre></td></tr></table>
</div></div></div>
<h5 id="auto-merge">Auto-Merge</h5>
<p>The two changes touch different rows, so both merges are clean and go through without human verification and are <strong>auto-merged</strong>. There&rsquo;s three-way and row-level merging, one atomic commit each:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-bash"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">omnigraph branch merge agent/inbox-scan --into main --as agent-inbox --store graphs/crm.omni
</span></span><span class="line"><span class="cl"><span class="c1"># merged agent/inbox-scan into main: fast_forward</span></span></span></code></pre></td></tr></table>
</div></div></div>
<h5 id="conflict-handling">Conflict Handling</h5>
<p>The interesting case is <strong>when writers disagree</strong> (<code>make conflict</code> in the repo): the inbox agent flags Nordbahn as <code>hot</code> (&ldquo;CEO replied today&rdquo;) while a CRM-hygiene agent downgrades the same row to <code>cold</code> (&ldquo;no touchpoint in 60 days&rdquo;). The first merge lands, and the second is blocked:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Error: merge conflicts: [MergeConflict { row_id: &#34;com-nordbahn&#34;,
</span></span><span class="line"><span class="cl">       kind: DivergentUpdate, ... }]</span></span></code></pre></td></tr></table>
</div></div></div>
<p>Nothing is published, and there&rsquo;s <strong>no silent last-write-wins</strong>. Instead, a human (or a policy/data contract) decides. And because every write records an actor (<code>--as agent-inbox</code>), <code>omnigraph commit list</code> gives an audit trail where &ldquo;who set this client to cold, and when?&rdquo; has an actual answer. You can find the full captured run of both cases, <a href="https://github.com/ssp-data/company-brain-crm/blob/main/DEMO.md#make-agents" target="_blank" rel="noopener noreffer">the auto-merge</a> and <a href="https://github.com/ssp-data/company-brain-crm/blob/main/DEMO.md#make-conflict" target="_blank" rel="noopener noreffer">the blocked conflict</a>, in the repo.</p>
<p>This is a fictive demo, with very little data to illustrate the concept, but I hope you got the gist of how this enables totally different context-related use cases, and eases life with auto-merge and agent help to update stale documentation and context.</p>
<blockquote>
<p>[!example] <a href="https://github.com/ModernRelay/omnigraph-cookbooks/tree/main/second-brain" target="_blank" rel="noopener noreffer">Another Second Brain approach with OmniGraph</a></p>
<p>If you want another personal second brain example, and an application with OmniGraph as a personal life ontology, this showcases a typed graph to answer questions like:</p>
<ul>
<li>Who haven&rsquo;t I talked to in a while that I want to?</li>
<li>What did Sarah recommend that I haven&rsquo;t read yet?</li>
<li>What do I still owe Theo?</li>
<li>What&rsquo;s on my mind about parenting / work / health?</li>
</ul>
</blockquote>
<h2 id="building-enterprise-brains-and-whats-next">Building Enterprise Brains, and What&rsquo;s Next?</h2>
<p>I hope you got a good understanding of the difference between a personal wiki and a fully automated LLM wiki where agents write back. And why features such as versioned branches, open-format object storage, and types help form a graph that describes rich context much better than plain text or scattered, outdated documentation ever could, especially with the help of agents: collecting and scraping useful information, but also updating existing context and raising awareness where contradictions in the data have been made.</p>
<p>This new approach is worth remembering for building an enterprise company brain. It&rsquo;s not only embeddings you search by similarity, but also typed, declared knowledge plus inferred similarity, versioned like code, so a hundred agents can write to it while humans keep it from drifting into noise, by reviewing what merges back to the main branch.</p>
<p>In the end, it&rsquo;s the same bet I made when I moved my personal brain to plain Markdown. Open files <a href="https://stephango.com/file-over-app" target="_blank" rel="noopener noreffer">outlive every tool</a> that reads them, and a company brain stored as open files in your own bucket will survive whatever CRM or wiki is used today. What&rsquo;s new is that the files now carry types, history, and a merge workflow, so the knowledge no longer depends on one careful owner. My Obsidian vault needs me, the single user. An enterprise company brain, <a href="https://modernrelay.com/articles/graph-as-code" target="_blank" rel="noopener noreffer">a graph database designed for agents</a>, kept fresh by agents and reviewed by humans, mostly needs someone to hit &ldquo;merge&rdquo;.</p>
<p>This difference compounds and can be the key to managing ever-growing information, moving from a wiki you maintain to an enterprise brain your company actually uses.</p>
<hr>
<p>If you want to get started, the <a href="https://github.com/ModernRelay/omnigraph-cookbooks" target="_blank" rel="noopener noreffer">omnigraph-cookbooks</a> are the fastest path. Feed one to the agent of your choice and get ready-to-run graphs with real seed data (company brain, VC operating system, pharma and industry intel). And for a deeper discussion, co-founders Andrew and Ragnor talk it through with Stanislav Kozlovski in <a href="https://x.com/kozlovski/status/2066182400002990216" target="_blank" rel="noopener noreffer">why agents need typed graphs to coordinate</a>.</p>
<!-- * * * -->
<!-- <pre class=""><em>Full article published at <a href="https://www.modernrelay.com..." target="_blank" rel="noopener noreferrer">Kaelio.com</a> - written as part of <a href="/services/">my services</a></em></pre> -->
]]></description>
</item>
<item>
    <title>The Grammar of Data: From Definition to Execution</title>
    <link>https://www.ssp.sh/blog/from-definition-to-execution-grammar-of-data/</link>
    <pubDate>Mon, 14 Sep 2026 08:00:08 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/from-definition-to-execution-grammar-of-data/</guid><enclosure url="https://www.ssp.sh/blog/from-definition-to-execution-grammar-of-data/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>In <a href="/blog/grammar-data-engineering/" rel="">Part 1</a>, we discovered the grammar for data: a way to define a data project with its complex requirements and how we define it declaratively as a grammar in one sentence with nouns (sources), transformations (verbs), templates, and modifiers, essentially being able to define it once and run it anywhere with different execution engines.</p>
<p>This Part 2 will demonstrate how this looks in a data engineering digest project where we process data from RSS feeds, a live Bluesky firehose, and GitHub datasets, and find trends with an all-integrated horizontal data architecture running xorq based on the grammar described.</p>
<p>We use dlt for ingestion (outside the grammar), then Ibis, DataFusion/DuckDB/Snowflake for the engine, a cataloging feature to compress and discover metrics, and a small ML job. This article will guide you through that project and explain why xorq and the grammar of data are helpful to you.</p>
<blockquote>
<p>[!Note] Want to jump right into the code: GitHub Repo<br>
Then follow along, the repository is at <a href="https://github.com/ssp-data/de-ecosystem-digest" target="_blank" rel="noopener noreffer">de-ecosystem-digest</a>, the showcase we will go through as an example for the grammar of data below.</p>
</blockquote>
<h2 id="the-grammar-of-data-in-action">The Grammar of Data in Action</h2>
<p>As a reminder, the grammar dedicated to data consists of these parts and constructs a full sentence as our data project:</p>













  

























<figure><span class="img-expandable" data-full="/blog/from-definition-to-execution-grammar-of-data/grammar-overview.png">
<a target="_blank" href="/blog/from-definition-to-execution-grammar-of-data/grammar-overview.png" title="The project read as one sentence - five grammar parts · one named, executable, re-runnable expression">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">The project read as one sentence - five grammar parts · one named, executable, re-runnable expression</figcaption>
</figure>
<p>In our <a href="https://github.com/ssp-data/de-ecosystem-digest" target="_blank" rel="noopener noreffer">data engineering digest example project</a> we create a data engineering digest based on my <a href="https://www.ssp.sh/brain/rss-feeds-for-data-engineering/" target="_blank" rel="noopener noreffer">DE RSS Feeds</a> I collected over the years, Bluesky posts, raw PyPI downloads and GitHub Archive events as source data that we ingest with <a href="https://github.com/dlt-hub/dlt" target="_blank" rel="noopener noreffer">dlt</a>. Here&rsquo;s an overview of the project:</p>













  

























<figure><span class="img-expandable" data-full="/blog/from-definition-to-execution-grammar-of-data/grammar-of-data-ececution.png">
<a target="_blank" href="/blog/from-definition-to-execution-grammar-of-data/grammar-of-data-ececution.png" title="Grammar of Data executed as a Sentence.">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Grammar of Data executed as a Sentence.</figcaption>
</figure>
<p><em>Model once, represent everywhere - the transformation never changes, only the engine binding does | Read left to right: every part of speech maps to a xorq call</em></p>
<p>We transform the data with xorq expressions (mutate, filter, group by, aggregate, order by), use templates to bind the sentence to any repo (dbt-core, polars, &hellip;) and modifiers to bind the engine (or a fitted ML model), and manifest it as a unique hash. Each named expression can be registered as a <strong>content-addressed, git-versioned catalog entry</strong> with the <a href="https://docs.xorq.dev/api_reference/cli/index.html#catalog" target="_blank" rel="noopener noreffer">catalog</a> being the shelf of all of them (such as <code>star_velocity_30d</code>, <code>download_trend_90d</code>), each reproducible on its own because xorq bundles the source read at build time.</p>
<p>Then we run those reusable metric definitions on any engine with pre-existing pipelines to make it easier to run with <code>make preview</code>, which executes every named expression, while <code>make catalog</code> registers the curated ones as versioned entries. The default engine is DataFusion, but I added DuckDB and Snowflake, using xorq&rsquo;s <strong>multi-compute engine capabilities</strong>.</p>
<h3 id="mapping-the-grammar-to-xorq">Mapping the Grammar to Xorq</h3>
<p>To illustrate the grammar and expression of the grammar in plain Python, here is how <a href="https://github.com/ssp-data/de-ecosystem-digest/blob/main/src/de_ecosystem/catalog/github.py" target="_blank" rel="noopener noreffer">github.py</a> could look, in six lines:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-python"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">star_velocity_30d</span><span class="p">(</span><span class="n">con</span><span class="p">,</span> <span class="n">repo</span><span class="p">):</span>          <span class="c1"># TEMPLATE: bind this sentence to any repo</span>
</span></span><span class="line"><span class="cl">    <span class="n">cutoff</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">con</span><span class="o">.</span><span class="n">table</span><span class="p">(</span><span class="s2">&#34;raw_github_events&#34;</span><span class="p">)</span>      <span class="c1"># NOUN: a lazy pointer, no computation yet</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">t</span><span class="o">.</span><span class="n">filter</span><span class="p">([</span>                          <span class="c1"># VERB</span>
</span></span><span class="line"><span class="cl">            <span class="n">t</span><span class="o">.</span><span class="n">repo_name</span> <span class="o">==</span> <span class="n">repo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">t</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="s2">&#34;WatchEvent&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">t</span><span class="o">.</span><span class="n">created_at</span> <span class="o">&gt;</span> <span class="n">cutoff</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">mutate</span><span class="p">(</span><span class="n">week</span><span class="o">=</span><span class="n">t</span><span class="o">.</span><span class="n">created_at</span><span class="o">.</span><span class="n">truncate</span><span class="p">(</span><span class="s2">&#34;W&#34;</span><span class="p">))</span>   <span class="c1"># VERB</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">group_by</span><span class="p">(</span><span class="s2">&#34;week&#34;</span><span class="p">)</span>                          <span class="c1"># VERB</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">agg</span><span class="p">(</span><span class="n">stars</span><span class="o">=</span><span class="n">t</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">count</span><span class="p">())</span>                   <span class="c1"># VERB</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s2">&#34;week&#34;</span><span class="p">)</span>                          <span class="c1"># VERB</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>Every one of these functions and the <a href="https://github.com/ssp-data/de-ecosystem-digest/blob/main/Makefile" target="_blank" rel="noopener noreffer">Makefile</a> lets us run the grammar as steps of the grammatical grammar, building a sentence like this. I added this for illustration, but as an overview, if we map the commands to <strong>a xorq call</strong>, we can see the connection from the grammar of data to the xorq function:</p>
<table>
	<thead>
			<tr>
					<th><code>make</code> target</th>
					<th>xorq / Python call</th>
					<th>grammar part</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>make noun</code></td>
					<td><code>con.table(...)</code></td>
					<td>noun (source)</td>
			</tr>
			<tr>
					<td><code>make verb</code></td>
					<td><code>.filter/.mutate/.agg</code> (deferred expr)</td>
					<td>verbs (transform)</td>
			</tr>
			<tr>
					<td><code>make template</code></td>
					<td><code>star_velocity_30d(con, repo)</code></td>
					<td>template (bind by arg)</td>
			</tr>
			<tr>
					<td><code>make modifier</code></td>
					<td><code>settings.backend(engine)</code></td>
					<td>modifier (engine/fit)</td>
			</tr>
			<tr>
					<td><code>make lineage</code></td>
					<td><code>expr.op()</code> / <code>ibis.to_sql</code> / <code>expr.ls</code></td>
					<td>lineage (what xorq sees)</td>
			</tr>
			<tr>
					<td><code>make manifest</code></td>
					<td><code>xorq build expr.py -e star_velocity</code></td>
					<td>model once → expr.yaml</td>
			</tr>
			<tr>
					<td><code>make catalog</code></td>
					<td><code>xorq catalog add … → ./catalog</code></td>
					<td>versioned entry store</td>
			</tr>
			<tr>
					<td><code>make run-sentence</code></td>
					<td><code>digest.main()</code> → <code>.execute()</code></td>
					<td>execute the sentence</td>
			</tr>
			<tr>
					<td><code>make engines</code></td>
					<td><code>settings.backend(x)</code> + <code>expr.execute()</code></td>
					<td>represent everywhere</td>
			</tr>
	</tbody>
</table>
<blockquote>
<p>[!note] Ingestion and installation are excluded on purpose here</p>
<p>To initialize, we also need <code>make install</code> to install dependencies and <code>make ingest</code> to load data with dlt locally. <code>make run-sentence</code> or <code>make full-pipeline</code> runs the full grammar of data. Additional commands <code>preview</code> <code>catalog</code> <code>catalog-run</code> <code>summary</code> <code>digest</code> <code>ml</code> <code>test</code> <code>clean</code> are added separately.</p>
</blockquote>
<h3 id="the-data-engineering-digest-what-we-found">The Data Engineering Digest: What We Found</h3>
<p>If we run the <a href="https://github.com/ssp-data/de-ecosystem-digest" target="_blank" rel="noopener noreffer">demo project</a> with the 90-day windows (PyPI max provides this window without storing data ourselves), we get a couple of interesting insights that this demo project produces from <strong>digesting the full Data Engineering ecosystem</strong>. The digest ranks tools and terms of data engineering by their <strong>momentum</strong> with PyPI download growth and GitHub data, and enriches each tool with its Bluesky chatter on socials<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>This is how it looks with <code>make digest</code>:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-text"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">package          growth_pct  recent_daily  total_downloads   buzz
</span></span><span class="line"><span class="cl">xorq                  130.9           968          125,104     10
</span></span><span class="line"><span class="cl">ibis-framework         49.5        90,384       13,655,147     15
</span></span><span class="line"><span class="cl">sqlglot                44.7     2,484,432      379,638,408     17
</span></span><span class="line"><span class="cl">duckdb                 42.5     1,712,837      263,396,944  3,200
</span></span><span class="line"><span class="cl">dagster                39.7       283,339       43,951,132    275
</span></span><span class="line"><span class="cl">polars                 35.8     2,160,110      339,334,602     61
</span></span><span class="line"><span class="cl">pydantic               30.0    35,567,354    5,688,585,163     90
</span></span><span class="line"><span class="cl">sqlmesh                28.6        17,964        2,885,574     13
</span></span><span class="line"><span class="cl">pyiceberg              24.4     1,300,103      211,933,200    640
</span></span><span class="line"><span class="cl">prefect                19.7       441,434       73,236,617     38
</span></span><span class="line"><span class="cl">dbt-core                9.9     3,527,875      609,587,211    369
</span></span><span class="line"><span class="cl">apache-airflow          8.6       702,582      122,123,125    113
</span></span><span class="line"><span class="cl">dlt                   -13.8       237,792       46,500,226    178</span></span></code></pre></td></tr></table>
</div></div></div>
<p>Interesting to see that we get a <strong>rise of the dataframe &amp; query engines</strong>: the fastest-growing DE packages over the last 90 days are all query/dataframe engines:</p>
<ul>
<li><code>ibis-framework</code> +49%, <code>sqlglot</code> +45%, <code>DuckDB</code> +42%, <code>Polars</code> +36%.</li>
</ul>
<p>One caveat: I included xorq. It has the biggest growth, but it&rsquo;s also the smallest package overall and still early, so the growth can have more spikes (we went from ~400 to ~968). And it&rsquo;s worth mentioning that xorq, the tool we use for the grammar series, uses and is built on <code>ibis</code>, its great expression layer, as xorq builds on a rising dataframe for a composable, in-process engine.</p>
<p>We also see that <strong>sqlmesh keeps climbing</strong> even after the Fivetran acquisition and dbt Labs joining Fivetran:</p>
<ul>
<li><code>sqlmesh</code> grew +28.6% over 90 days, while <code>dbt-core</code> grew the slowest of the pack (+9.9%).</li>
</ul>
<p>Not surprising, <strong>DuckDB wins social media</strong> attention. On Bluesky, its buzz score is 3200, 5× more than the next tool (pyiceberg 640, dbt 369). DuckDB is the tool that is both growing fast and the &ldquo;loudest&rdquo;.</p>
<p><strong>Number 1</strong> by raw downloads <strong>is pydantic</strong>. By absolute volume, pydantic tops everything at 5.64B downloads (~9× dbt-core&rsquo;s 610M):</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-text"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">── Naive leaderboard: raw PyPI downloads (all-time) ──
</span></span><span class="line"><span class="cl">package            downloads
</span></span><span class="line"><span class="cl">pydantic       5,639,574,035
</span></span><span class="line"><span class="cl">dbt-core         603,522,561
</span></span><span class="line"><span class="cl">sqlglot          376,710,633
</span></span><span class="line"><span class="cl">polars           335,901,439
</span></span><span class="line"><span class="cl">duckdb           261,307,529</span></span></code></pre></td></tr></table>
</div></div></div>
<p>This is probably because [Pydantic]https://github.com/pydantic/pydantic) is powering half of PyData while it doesn&rsquo;t really have a lot of hype, but is a <a href="https://www.ssp.sh/brain/data-engineering-toolkit/" target="_blank" rel="noopener noreffer">Data Engineering Toolkit</a> for data validation and settings management using Python type annotations, used by any data engineer. It&rsquo;s a part of the grammar that makes sure re-runs run deterministically.</p>
<blockquote>
<p>[!note] What the blogs (RSS) say<br>
My RSS feeds were the noisiest signal: titles skew to whoever writes the most, e.g. Mr. Robin Moffatt (rmoff&rsquo;s random ramblings alone are ~690 of ~1,600 articles) 😉. General sentiment clusters around the incumbents (dbt, dagster, Spark, Snowflake), while the surging engines (polars, SQLMesh) are barely mentioned. Blog coverage <em>lags</em> the download signal, which is precisely why the digest triangulates four sources instead of trusting just one.</p>
</blockquote>
<h2 id="the-whole-stack-in-one-file-stackyaml-and-the-exchangeable-engine">The Whole Stack in One File: <code>stack.yaml</code> and the Exchangeable Engine</h2>
<p>The key is really that model and execution are separated. Once the stack is defined (in our demo project I used <code>stack.yaml</code> as a declarative config), we can just change the engine by editing a YAML file, and everything else stays the same:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-yaml"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">engine</span><span class="p">:</span><span class="w"> </span><span class="l">datafusion             </span><span class="w"> </span><span class="c"># duckdb | datafusion | snowflake  &lt;- swap the engine here</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">db_path</span><span class="p">:</span><span class="w"> </span><span class="l">de_ecosystem.duckdb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">sources</span><span class="p">:</span><span class="w">                        </span><span class="c"># nouns — dlt ingests these (outside the grammar)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">bluesky</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">max_pages</span><span class="p">:</span><span class="w"> </span><span class="m">40</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">github</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">slice</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;data/raw/gharchive/*.json.gz&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">momentum</span><span class="p">:</span><span class="w">                       </span><span class="c"># the digest metric</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">window_days</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">               </span><span class="c"># 90 = laptop pulse; 365 = warehouse year-in-review</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">tools</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">dbt-core, dagster, dlt, ibis-framework, xorq, apache-airflow, polars,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="l">duckdb, great-expectations, pyiceberg, prefect, mage-ai, sqlmesh,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="l">soda-core, pydantic, sqlglot]</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>Imagine in your deploy scripts for <code>dev</code> you&rsquo;d use DataFusion and on <code>prod</code> you&rsquo;d just specify Snowflake as the variable. No implementation code is touched as in a typical imperative workflow.</p>
<p>With <code>make manifest</code> we can compile the full data stack&rsquo;s expressions into a deferred execution file <code>builds/&lt;hash&gt;/expr.yaml</code>, which builds deterministically and is diffable. The demo shows that point well: if we make the above engine change to <code>engine=duckdb</code> from <code>datafusion</code> and rebuild, the <em>semantic</em> change shows up as a reviewable <code>git diff</code>.</p>
<p>Let&rsquo;s run manifest with <code>engine: datafusion</code>:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">make manifest
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl">Written &#39;star_velocity&#39; to builds/10671a1c33cf
</span></span><span class="line"><span class="cl">builds/10671a1c33cf</span></span></code></pre></td></tr></table>
</div></div></div>
<p>Now changing <code>engine: duckdb</code> and re-running:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">make manifest
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl">Written &#39;star_velocity&#39; to builds/f02f2c4dca81
</span></span><span class="line"><span class="cl">builds/f02f2c4dca81</span></span></code></pre></td></tr></table>
</div></div></div>
<p>The diff shows a couple of interesting bits, e.g. xorq changed the scale for timestamps for duckdb (see both files <a href="https://github.com/ssp-data/de-ecosystem-digest/blob/dde2fbfc19948a3147178bd36781247a8d78e98a/builds/10671a1c33cf/expr.yaml#L16" target="_blank" rel="noopener noreffer">datafusion</a> and <a href="https://github.com/ssp-data/de-ecosystem-digest/blob/dde2fbfc19948a3147178bd36781247a8d78e98a/builds/f02f2c4dca81/expr.yaml#L16" target="_blank" rel="noopener noreffer">DuckDB</a>):</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">  -      scale: 9      # datafusion → nanosecond timestamps
</span></span><span class="line"><span class="cl">  +      scale: ~      # duckdb → microsecond timestamps</span></span></code></pre></td></tr></table>
</div></div></div>
<p>The <code>profile.yaml</code> shows the literal change we did:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-fallback"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">  -  con_name: xorq_datafusion
</span></span><span class="line"><span class="cl">  -    config: ~
</span></span><span class="line"><span class="cl">  +  con_name: duckdb
</span></span><span class="line"><span class="cl">  +    database: &#34;:memory:&#34;
</span></span><span class="line"><span class="cl">  +    read_only: false</span></span></code></pre></td></tr></table>
</div></div></div>
<p>This shows that swapping the engine by configuration has a real impact: DataFusion carries Timestamp(scale=9) (nanoseconds) and DuckDB defaults to microseconds. The <strong>manifest captures that difference explicitly</strong> even before we run anything, <strong>reviewable instead of a silent runtime error</strong> through the built <a href="https://docs.xorq.dev/getting_started/your_first_expression.html" target="_blank" rel="noopener noreffer">expression graphs</a> before executing them with <strong>one expression, many engines</strong>.</p>
<h2 id="additional-capabilities-of-the-de-digest-project">Additional Capabilities of the DE Digest Project</h2>
<p>Apart from the grammar we look at, the project comes with catalog and ML functions to showcase the full capabilities of xorq and what you typically want to do in a data engineering project.</p>
<p>The full data lineage can also be tracked and shown.</p>
<h3 id="the-catalog-and-ml-capabilities">The Catalog and ML Capabilities</h3>
<p>The project has added a catalog to retrieve versioned entries of our metric and created artifacts. With <code>make catalog</code>, this project with xorq-catalog registers a curated set of expressions as <strong>versioned, content-addressed entries</strong> in a local, git-backed catalog at <code>./catalog</code>. The <code>catalog.yaml</code> manifest is the shelf where entries are addressed by content hash, and aliases are the human-readable handles:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-yaml"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">entries</span><span class="p">:</span><span class="w">                     </span><span class="c"># content hashes (a new hash = a new version)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">5adcf6bccba9            </span><span class="w"> </span><span class="c"># dbt-star-velocity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">a4f38c87079a            </span><span class="w"> </span><span class="c"># dbt-download-trend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">a636a49497fb            </span><span class="w"> </span><span class="c"># dbt-momentum</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># …</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">aliases</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dbt-star-velocity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dbt-download-trend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dbt-momentum</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">duckdb-buzz</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dbt-mentions</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">dbt-health</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">rising-tools</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>Each entry holds its own metadata sidecar with the <em>expression + metadata + cached result, addressed by hash</em>. Here is <code>dbt-download-trend</code>, the whole metric captured declaratively (kind, output schema, the compiled SQL over the source table, and the cache key):</p>
<div class="highlight"><div class="chroma"><div class="code-header language-yaml"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">md5sum</span><span class="p">:</span><span class="w"> </span><span class="l">5a7d75db1e0cf46dc59a713a6ffa9573</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">backends</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">xorq_datafusion]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">expr_metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">expr</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schema_out</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="l">timestamp(9)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">downloads</span><span class="p">:</span><span class="w"> </span><span class="l">int64</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">package</span><span class="p">:</span><span class="w"> </span><span class="l">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cache_keys</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">xorq_cache-snapshot-4788a41e541c0526df207922813c0377</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">relative_path</span><span class="p">:</span><span class="w"> </span><span class="l">parquet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sql_queries</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- - <span class="l">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">xorq_datafusion</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="p">|-</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        SELECT &#34;t0&#34;.&#34;date&#34;, &#34;t0&#34;.&#34;downloads&#34;, &#34;t0&#34;.&#34;package&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">        FROM &#34;raw_pypi_downloads&#34; AS &#34;t0&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">        WHERE &#34;t0&#34;.&#34;package&#34; = &#39;dbt-core&#39;
</span></span></span><span class="line"><span class="cl"><span class="sd">          AND &#34;t0&#34;.&#34;category&#34; = &#39;without_mirrors&#39;
</span></span></span><span class="line"><span class="cl"><span class="sd">          AND &#34;t0&#34;.&#34;date&#34; &gt; DATE_TRUNC(&#39;DAY&#39;, &#39;2026-05-14&#39;)
</span></span></span><span class="line"><span class="cl"><span class="sd">        ORDER BY &#34;t0&#34;.&#34;date&#34; ASC</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>Because xorq bundles the source read at build time, an entry is self-contained: <code>make catalog-run ALIAS=dbt-momentum</code> re-executes it with <strong>no re-ingest</strong>. Edit an expression (say <code>days=30 → 90</code>) and its content hash changes, so it registers as a <em>new</em> version while the old one stays retrievable.</p>
<p>The project also ships a small ML task (build feature matrix, split, fit a sklearn <code>LogisticRegression</code> wrapped in a xorq <code>Pipeline</code>, predict an adoption label) to mimic prediction and training of a real-life project. It could be interesting to add more sophisticated logic once more data is downloaded, and potentially even more sources are added.</p>
<p>With that example, we use the grammar of data with xorq to get essentially needed steps as part of the <a href="https://www.oreilly.com/library/view/fundamentals-of-data/9781098108298/ch02.html#the_data_engineering_lifecycle-id000095" target="_blank" rel="noopener noreffer">Data Engineering Lifecycle</a>. We can have full lineage, we get deterministic reruns, we get the metrics in the catalog. Plus, we get extensibility at each stage if we need it.</p>
<p>E.g. extend the metrics from &lsquo;catalog -&gt; <a href="https://github.com/boringdata/boring-semantic-layer" target="_blank" rel="noopener noreffer">Boring Semantic Layer</a>&rsquo;, or use dlt for ingestion as I did in this project to load data incrementally into a staging area.</p>
<h4 id="the-lineage">The Lineage</h4>
<p>For instance, <code>make lineage</code> shows everything xorq knows about a sentence <em>before</em> it touches a single row: its source nouns, output schema, bound engine, and the verbs compiled to SQL. This is how it looks:</p>
<div class="highlight"><div class="chroma"><div class="code-header language-text"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">LINEAGE — what xorq knows before any data is read:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;&gt; expr.op().find(DatabaseTable)   — the source nouns this expression reads:
</span></span><span class="line"><span class="cl">     [&#39;raw_github_events&#39;]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;&gt; expr.schema()                   — the output columns (resolved at build time):
</span></span><span class="line"><span class="cl">     ibis.Schema { week: timestamp; stars: int64 }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;&gt; expr.ls.backends                — the engine(s) bound to it (MODIFIER):
</span></span><span class="line"><span class="cl">     [&#39;Backend&#39;]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;&gt; ibis.to_sql(expr)               — the VERB chain, compiled to SQL:
</span></span><span class="line"><span class="cl">SELECT &#34;t1&#34;.&#34;week&#34;, COUNT(&#34;t1&#34;.&#34;id&#34;) AS &#34;stars&#34;
</span></span><span class="line"><span class="cl">FROM ( SELECT DATE_TRUNC(&#39;WEEK&#39;, &#34;created_at&#34;) AS &#34;week&#34;, &#34;id&#34;
</span></span><span class="line"><span class="cl">       FROM &#34;raw_github_events&#34;
</span></span><span class="line"><span class="cl">       WHERE &#34;repo_name&#34; = &#39;dbt-labs/dbt-core&#39; AND &#34;type&#34; = &#39;WatchEvent&#39; )
</span></span><span class="line"><span class="cl">GROUP BY &#34;t1&#34;.&#34;week&#34; ORDER BY &#34;t1&#34;.&#34;week&#34;</span></span></code></pre></td></tr></table>
</div></div></div>
<p>If you use the xorq Desktop app, this is integrated into a nice UI like this:</p>













  

























<figure><span class="img-expandable" data-full="/blog/from-definition-to-execution-grammar-of-data/xorq-desktop-lineage.png">
<a target="_blank" href="/blog/from-definition-to-execution-grammar-of-data/xorq-desktop-lineage.png" title="Lineage view in xorq&rsquo;s upcoming Desktop app">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Lineage view in xorq&rsquo;s upcoming Desktop app</figcaption>
</figure>
<h3 id="machine-learning-as-another-modifier">Machine Learning as Another Modifier</h3>
<p>The tool-adoption model reuses the same four parts of speech: <code>fit(...)</code> attaches a <strong>modifier</strong> - the fitted model rides along as metadata (xorq tracks a <code>training_hash</code>) without changing what the expression computes, which is precisely <a href="/blog/grammar-data-engineering/" rel="">Part 1</a>&rsquo;s definition of a modifier. <code>predict(...)</code> is just a verb returning an Ibis table expression. And because predict is an expression, <code>xorq build</code> can manifest the <em>inference</em> pipeline too. Deploying a model collapses into the same <code>write → manifest → execute</code> cycle as deploying a metric.</p>
<blockquote>
<p>[!example] Add Semantic Layer<br>
Here we use the inbuilt catalog and metrics are defined as expressions in Ibis. If you like, you could lift the catalog metrics into the Boring Semantic Layer for dimensions/measures. BSL is built by Hussain, the creator of xorq, and is tightly integrated. Check <a href="https://github.com/boringdata/boring-semantic-layer" target="_blank" rel="noopener noreffer">boring-semantic-layer</a> if that is of interest, or check a recent article I wrote, <a href="https://motherduck.com/blog/semantic-layer-duckdb-tutorial/" target="_blank" rel="noopener noreffer">Why Semantic Layers Matter</a>, with a practical example.</p>
</blockquote>
<h2 id="so-what-did-we-learn-applying-the-grammar-for-data">So What Did We Learn Applying the Grammar for Data?</h2>
<p><a href="/blog/grammar-data-engineering/" rel="">Part 1</a> introduced the concept of grammar for data. With the data engineering digest project, we applied it to a demo project to capture the momentum of a tool in the data ecosystem. We mapped xorq&rsquo;s features to the grammar to illustrate it better, and we&rsquo;ve built a deterministic and versionable data stack with a single <code>stack.yaml</code>, providing the end-to-end data capabilities a data engineering project needs. Everything supports the case for a grammar for data.</p>
<p>xorq gives us the bottom-up approach, working with our data, mapping all parts of the DE lifecycle, running it locally on multiple engines and discovering errors at pre-run time. Ultimately, <a href="https://www.rilldata.com/blog/what-shifting-left-means-and-why-it-matters-for-data-stacks" target="_blank" rel="noopener noreffer">shifting left</a> once more.</p>
<p>The grammar buys us two guarantees: answers that are <em>faithful</em> to the expression that produced them, and <em>reproducible</em> whenever we rerun it. But that doesn&rsquo;t always mean correct. For example, the expression can still encode the wrong interpretation of the question. That third guarantee comes from reviewed definitions (the catalog entries and semantic models we built above). You can learn more about it in a follow-up article <a href="https://xorq.dev/blog/faithful-reproducible-wrong/" target="_blank" rel="noopener noreffer">Faithful, Reproducible, Wrong</a>, where a checker plus a reviewed semantic model takes an agent from 4/100 to 100/100 correct answers on the same question.</p>
<p>Another related question is: if the grammar defines and the manifest records, who verifies it? That&rsquo;s what we look at next in this series. You can get a sneak peek with a checker inside an agent harness (<a href="https://pi.dev/" target="_blank" rel="noopener noreffer">pi</a>), where every quantitative claim must be discharged by rerunning a content-addressed expression with its lineage intact. You can watch the loop in action in <a href="https://asciinema.org/a/1263221" target="_blank" rel="noopener noreffer">this recording</a> and reproduce it from the <a href="https://github.com/xorq-labs/pi-xorq-verification-example" target="_blank" rel="noopener noreffer">pi-xorq-verification-example</a> repo. More on verification in Part 3.</p>
<hr>
<p>If you got interested and want to know more about how xorq works, check out the <a href="https://docs.xorq.dev/" target="_blank" rel="noopener noreffer">docs</a>, or the open source repo on <a href="https://github.com/xorq-labs/xorq" target="_blank" rel="noopener noreffer">GitHub</a>.</p>
<p>Also check out the <a href="https://xorq.dev/" target="_blank" rel="noopener noreffer">upcoming xorq Desktop app</a> (join the waitlist), which targets data analysts from the top down. It&rsquo;s a desktop app on macOS and a trusted harness. It has additional features that do a verification check and more.</p>
<h2 id="appendix">Appendix</h2>
<p>Additional project information for running the GitHub project, if you are interested in running it yourself and having a closer look.</p>
<h3 id="quick-note-on-dlt-ingestion">Quick Note on dlt Ingestion</h3>
<p>All four sources use idempotent upsert, so re-running only adds new rows / updates existing ones (never duplicates):</p>
<ul>
<li><strong>RSS + Bluesky</strong> → dlt <code>write_disposition=&quot;merge&quot;</code> on <code>primary_key=&quot;id&quot;</code> (article URL / post URI)</li>
<li><strong>PyPI</strong> → <code>INSERT OR REPLACE</code> on <code>(package, date, category)</code></li>
<li><strong>GitHub</strong> → <code>INSERT OR IGNORE</code> on event <code>id</code></li>
</ul>
<h3 id="loading-1-year-on-snowflake">Loading 1 Year on Snowflake</h3>
<p>To run a full year or more, just use Snowflake, for example by installing <code>xorq[snowflake]</code> and ingesting the data into Snowflake. Re-running is always safe (idempotent merge), so just pull more and re-run. Each source has its own ceiling:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-bash"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. More Bluesky history — edit src/de_ecosystem/ingest/bluesky.py</span>
</span></span><span class="line"><span class="cl"><span class="nv">MAX_PAGES</span> <span class="o">=</span> <span class="m">200</span>            <span class="c1"># 40 → 200, pages much further back in time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. More GitHub events — download extra hours into data/raw/gharchive/ (gitignored)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> h in <span class="k">$(</span>seq <span class="m">0</span> 23<span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  wget -nc -P data/raw/gharchive <span class="s2">&#34;https://data.gharchive.org/2026-08-05-</span><span class="nv">$h</span><span class="s2">.json.gz&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Re-ingest (safe, merges) and re-run the whole sentence</span>
</span></span><span class="line"><span class="cl">make ingest
</span></span><span class="line"><span class="cl">make run-sentence</span></span></code></pre></td></tr></table>
</div></div></div>
<p>The one limit is that the live <code>pypistats.org</code> API only serves ~180 days of downloads, so PyPI momentum caps at a 90-day-vs-prior-90-day window.</p>
<p>A <em>full-year</em> digest needs a source that actually stores that history, and that is where Snowflake (or BigQuery&rsquo;s public <code>bigquery-public-data.pypi.file_downloads</code>) comes in, both holding years of daily download stats.</p>
<p>Because the grammar separates <em>what</em> from <em>where</em>, you don&rsquo;t rewrite the metric. You just point the <strong>noun</strong> at the warehouse and run the same sentence:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-bash"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uv sync                       <span class="c1"># the snowflake driver ships as a base dependency</span>
</span></span><span class="line"><span class="cl"><span class="c1"># put SNOWFLAKE_ACCOUNT / USER / PASSWORD / ROLE / DATABASE / WAREHOUSE / SCHEMA</span>
</span></span><span class="line"><span class="cl"><span class="c1"># in .env (or export them in your shell)</span>
</span></span><span class="line"><span class="cl">make engines                  <span class="c1"># runs star_velocity on DuckDB, DataFusion AND Snowflake</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>And with the same verb and only the engine binding changed:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-python"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">con</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">backend</span><span class="p">(</span><span class="s2">&#34;snowflake&#34;</span><span class="p">)</span>                             <span class="c1"># xo.snowflake.connect_env()</span>
</span></span><span class="line"><span class="cl"><span class="n">momentum</span> <span class="o">=</span> <span class="n">download_momentum</span><span class="p">(</span><span class="n">con</span><span class="p">,</span> <span class="s2">&#34;duckdb&#34;</span><span class="p">,</span> <span class="n">window_days</span><span class="o">=</span><span class="mi">365</span><span class="p">)</span>    <span class="c1"># a full year</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>The catalog code itself has no engine awareness. <code>settings.backend(&quot;duckdb&quot; | &quot;datafusion&quot; | &quot;snowflake&quot;)</code> is the only thing that changes. Define once, represent it everywhere: your laptop for a 90-day pulse, a warehouse for the year-in-review.</p>
<p>The one-time grants (<code>de_digest</code> must exist and <code>DLT_LOADER_ROLE</code> needs <code>CREATE TABLE</code> <strong>and</strong> <code>CREATE STAGE</code>, since the loader stages the tables before copying them in):</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-sql"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- run once as ACCOUNTADMIN; the demo materialises the 4 raw tables into de_digest.PUBLIC
</span></span></span><span class="line"><span class="cl"><span class="n">USE</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">ACCOUNTADMIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">DATABASE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">de_digest</span><span class="p">;</span><span class="w">   </span><span class="c1">-- PUBLIC schema is created automatically
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">USAGE</span><span class="w">          </span><span class="k">ON</span><span class="w"> </span><span class="k">DATABASE</span><span class="w"> </span><span class="n">de_digest</span><span class="w">       </span><span class="k">TO</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">USAGE</span><span class="w">          </span><span class="k">ON</span><span class="w"> </span><span class="k">SCHEMA</span><span class="w">   </span><span class="n">de_digest</span><span class="p">.</span><span class="k">PUBLIC</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w">   </span><span class="k">ON</span><span class="w"> </span><span class="k">SCHEMA</span><span class="w">   </span><span class="n">de_digest</span><span class="p">.</span><span class="k">PUBLIC</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">CREATE</span><span class="w"> </span><span class="n">STAGE</span><span class="w">   </span><span class="k">ON</span><span class="w"> </span><span class="k">SCHEMA</span><span class="w">   </span><span class="n">de_digest</span><span class="p">.</span><span class="k">PUBLIC</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="p">;</span><span class="w">  </span><span class="c1">-- adbc/pandas bulk
</span></span></span><span class="line"><span class="cl"><span class="k">load</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">USAGE</span><span class="p">,</span><span class="w"> </span><span class="n">OPERATE</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">WAREHOUSE</span><span class="w"> </span><span class="n">COMPUTE_WH</span><span class="w">      </span><span class="k">TO</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">DLT_LOADER_ROLE</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="k">USER</span><span class="w"> </span><span class="n">loader</span><span class="p">;</span><span class="w">   </span><span class="c1">-- if not already</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>And the tables it created in Snowflake if everything works correctly:</p>
<p><span class="img-expandable" data-full="/blog/from-definition-to-execution-grammar-of-data/snowflake-tables.webp"><a target="_blank" href="/blog/from-definition-to-execution-grammar-of-data/snowflake-tables.webp" title="snowflake-tables.webp" >
        
    </a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span></p>
<hr>
<pre class=""><em>Full article published at <a href="https://xorq.dev/blog/grammar-of-data-part2-definition-execution/" target="_blank" rel="noopener noreferrer">xorq.dev</a> - written as part of <a href="/services/">my services</a></em></pre> 
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Bluesky has a firehose and can simply be queried with DuckDB, e.g. see <a href="https://www.ssp.sh/brain/querying-bluesky-with-duckdb-and-sql/" target="_blank" rel="noopener noreffer">Querying Bluesky with DuckDB and SQL</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>Context, Semantics, and Ontology: A Primer for the Agentic Era</title>
    <link>https://www.ssp.sh/blog/semantic-context-layer-primer/</link>
    <pubDate>Sat, 12 Sep 2026 06:40:08 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/semantic-context-layer-primer/</guid><enclosure url="https://www.ssp.sh/blog/semantic-context-layer-primer/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>There&rsquo;s so much talk about new ways of working with agent engineering supported workflows. New models are independently creating new metrics and transformations, finding gaps in the business data, reviewing the SQL they write, and verifying everything works with your data platform.<br>
All of it is autonomous, so one might say, why do we still need BI tools, or therefore a semantic layer, or even a context layer? It&rsquo;s hard to predict the future, but as someone working with newer models as well as older ones, I could see that Fable for example is one-shotting work in minutes that previously took me many iterations with Opus 4.8 (below you see my recent Claude and Codex use (roborev only)). With the right workflows to brainstorm first a spec, then an implementation plan, it&rsquo;s really amazing how fast and precisely one can build with the right amount of inputs we give, and <a href="https://substack.com/home/post/p-189793289" target="_blank" rel="noopener noreffer">taste</a>.</p>
<p>So what then is left for us humans to do in the data work context? Many are defaulting to adding or curating context, ergo the <strong>rise of a context layer</strong>. I see even more talks about added Ontologies. Maybe you ask yourself, what is that even? Do we need all of it?</p>
<p>This article is a primer about the context layer, the difference between a classical semantic layer contained in every BI tool, and an external semantic layer.</p>
<blockquote>
<p>[!Note] The other Surface level<br>
Besides context, I&rsquo;d add that the other big surface level where a lot is currently happening, with human in the loop vs autonomous agents working, is verification, especially verification in data engineering work.</p>
</blockquote>
<h2 id="what-is-context-and-its-context-layer">What is Context, and Its Context Layer?</h2>
<p>But we can&rsquo;t talk about the new shiny context layer tools, ontologies or any other, before we define context, and how much context we need. If we go by the definition of the word context, <a href="https://dictionary.cambridge.org/dictionary/english/context" target="_blank" rel="noopener noreffer">Cambridge Dictionary</a> says:</p>
<blockquote>
<p>The situation within which something exists or happens, and that can help explain it</p>
</blockquote>
<p><a href="https://jessicatalisman.substack.com/" target="_blank" rel="noopener noreffer">Jessica Talisman</a>, who writes about semantics, the vocabulary and the intersections of ontologies, <a href="https://jessicatalisman.substack.com/p/the-context-problem" target="_blank" rel="noopener noreffer">defines</a> context in information science as:</p>
<blockquote>
<p>Describes the <strong>relational structure that holds meaning in place</strong>.</p>
</blockquote>
<p>This sounds very much like metadata, the data about the data we collect in logs, orchestration runs, table information, index information. But let&rsquo;s go one step further, what&rsquo;s a Context Layer?</p>
<h3 id="whats-a-context-layer">What&rsquo;s a Context Layer?</h3>
<p>There are multiple definitions out there, and bear in mind, this term is less than a year old, but Andreessen Horowitz <a href="https://a16z.com/your-data-agents-need-context/" target="_blank" rel="noopener noreffer">describes a &ldquo;Modern Context Layer&rdquo; to contain</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> three pieces:</p>
<blockquote>
<p>1) <strong>Accessing the right data</strong>. [&hellip;] we’d want to ensure the agent has access to all the data it needs [&hellip;] captured in internal systems, GDrive/Slack, etc.</p>
<p>2) <strong>Automated context construction</strong> [&hellip;] emphasis of <strong>focus should be on high signal context</strong> – for example, looking through past query history can be high signal in determining the most referenced tables and most common joins, and data modeling solutions like dbt or LookML can provide clear definitions for business metrics.</p>
<p>3) <strong>Human refinement</strong> – Automated context construction may be able to form a large portion of the context corpus, but it can’t create the full picture. [&hellip;]</p>
</blockquote>
<p>I defined it in <a href="https://www.kaelio.com/blog/building-a-context-layer-for-the-agentic-era" target="_blank" rel="noopener noreffer">Beyond the Semantic Layer</a> in a similar fashion:</p>
<blockquote>
<p>The context layer primarily <strong>supports the accuracy of SQL queries, continuous updates to the business context</strong>, and governance. Additionally, with a newer context layer, we can <strong>include more relevant business insights</strong> that are stored internally, usually in <strong>unstructured form</strong>, in tools like Notion as business documentation.</p>
</blockquote>
<p>So it seems clear, we focus on less &ldquo;classical&rdquo; data sources from databases, or structured tables, but more free-flow text that humans wrote in docs, Confluence, Notion.</p>
<h3 id="comparison-to-a-semantic-layer-and-how-they-work-together">Comparison to a Semantic Layer, and how They Work together</h3>
<p>Now that we defined context and its layer, it begs the question, what&rsquo;s the difference to a semantic layer (which, to some, might be another rather vague definition)? I know, I have <a href="https://www.ssp.sh/blog/rise-of-semantic-layer-metrics/" target="_blank" rel="noopener noreffer">written about</a> past BI integrated ones, and more modern ones like Cube, Malloy, dbt one, but it still seems everyone has a different definition.</p>
<p>The definition of a semantic layer, as we discussed in <a href="/blog/semantic-layer-duckdb/" rel="">Why Semantic Layers Matter</a> (including an example of how to build one), is defined by Julian Hyde, creator of Morel Language, and an early employee of Looker and LookML:</p>
<blockquote>
<p>A semantic layer, also known as a metrics layer, lies between business users and the database, and lets those users compose queries in the concepts that they understand. It also governs access to the data, manages data transformations, and can tune the database by defining materializations.<br>
Like many new ideas, the semantic layer is a distillation and evolution of many old ideas, such as query languages, multidimensional OLAP, and query federation.</p>
</blockquote>
<p>So that means, a <strong>semantic layer understands the semantics, the metrics and governance access</strong> and data transformation, and has a link to (multi-dimensional) OLAP and query languages, while the <strong>context layer is more focused on gathering context and refinement between humans and agents</strong>.</p>
<p>I see the two, a semantic layer and a context layer, working for distinct fields, and working hand in hand:</p>
<ul>
<li><strong>Semantic Layer</strong>: Definition is <strong>written as logic that compiles to SQL</strong>, like a metric or a dimension</li>
<li><strong>Context layer</strong>: Holds everything else: the rules that can&rsquo;t be explained, and everything that is <strong>unrelated to a dedicated query</strong>.</li>
</ul>
<h2 id="the-context-layer-and-agents-paradox">The Context Layer and Agents Paradox</h2>
<p>In a semantic layer, Notion Docs, Confluence pages, and other unstructured text didn&rsquo;t count as real data sources. There&rsquo;s no incremental load on Markdown, so we didn&rsquo;t include them.</p>
<p>There is also another angle in play. Before agents, it was hard to keep track of free flow Markdown text, finding out what has changed in the incremental load, and what&rsquo;s useful, what has the biggest signal as a16z said above. But with the help of agents, we can now integrate more. Agents can do the bulk of the work, and that&rsquo;s why context layers popped up in the first place.</p>
<p>It&rsquo;s a <strong>paradox</strong>: if we <strong>didn&rsquo;t have agents, we wouldn&rsquo;t need this much context</strong>. But we <strong>need more context because of agents</strong>, and we <strong>can only manage it because we have agents</strong>.</p>
<p>We can load more data than ever now <strong><em>because</em> agents do the integration work</strong> that wasn&rsquo;t feasible before, but we also need to load more data than ever because agents need to be smarter to use it well. The agent needs every bit of human-written information to increase its context and make better decisions, but that&rsquo;s only possible because of agents in the first place.</p>
<p>Jacob saw a similar paradox in <a href="https://analyticshour.io/2026/06/23/300-are-semantic-layers-really-necessary/" target="_blank" rel="noopener noreffer">Are Semantic Layers Really Necessary?</a> and says:</p>
<blockquote>
<p><strong>there&rsquo;s a little bit of a paradox</strong>:  you need some level of conformity to communicate well, but also if you have too much conformity, you have a commodity. You need space to have some differentiation.</p>
</blockquote>
<blockquote>
<p>[!note] The crucial part of how to include more unstructured &lsquo;Context&rsquo; is how the integration is updated continuously<br>
As with the above definition, I&rsquo;d 100% agree that the crucial part, and also the challenge, is the combination of agents with human inputs, as <a href="/blog/figma-for-agents-max-beauchemin/" rel="">data warehouse projects are all messy</a> as we recently discussed in the Maxime Beauchemin interview.</p>
</blockquote>
<h3 id="how-much-context-do-we-need-and-what-kind">How much Context Do We Need? And what Kind?</h3>
<p>That begs the question: How much context do we need, and what kind? Should we gather all Markdown files we find, or use the JIRA tickets where we defined our tasks?</p>
<p>And what&rsquo;s the quality of these JIRA tickets? They might be super up-to-date, but missing a whole lot of information, so what kind of &ldquo;context&rdquo; do we get from them, or do we want?</p>
<p>With agents, we could probably say the more context, the better, and hopefully the agent identifies the high-signal and valuable data out of empty Jira tickets 😉. But assuming we gathered all of the above context, how do we actually manage it? How do we update it, how do we make sure it&rsquo;s correct?</p>
<p>This is actually where we always land at the same fundamental questions of <em>Data Engineering Lifecycle</em> and <em>data governance</em>, especially in an enterprise setting with multiple actors and people. Because it&rsquo;s easy to build up context initially, once. But the hard part is updating it, maintaining it, having rules, having a governance strategy.</p>
<p>So the Context Layer integration, as well as the semantic layer, becomes a data governance and enterprise process definition problem.</p>
<h2 id="open-standards-for-metrics-and-data-quality">Open Standards for Metrics and Data Quality</h2>
<p>What helps us here are open standards. Luckily we just recently got the <strong>Open Semantic Interchange (OSI)</strong> standard, recently renamed to <a href="https://github.com/apache/ossie" target="_blank" rel="noopener noreffer">Apache Ossie</a>. They standardise the syntax around semantic layer declarative configuration. The goal is to have an <strong>open-source, vendor-agnostic specification for describing and exchanging semantic models</strong>.</p>
<p>Not that we end up with many different proprietary YAML definitions for LookML, Cube, dbt Semantic Layer, Snowflake&rsquo;s internal one, etc. as we have it today. If you write in the standard, it can be easily mapped to any semantic layer definition.</p>
<h3 id="metrics-to-measure-the-data-quality-of-context">Metrics to Measure the Data Quality of Context</h3>
<p>Another part of the puzzle and how good context layers will be, is the data quality. The outcome, the <strong>context itself, strongly depends on the quality</strong> and how up-to-date and <em>correct</em> the data is.</p>
<p>One thing I was pondering for a long time: how do we measure that? In my previous job we had a single test score to represent data quality, something like an <a href="https://www.rohde-schwarz.com/us/knowledge-center/videos/interview-nps-the-single-qoe-centric-network-score-from-rs_251220-621634.html" target="_blank" rel="noopener noreffer">Net Performance Score (NPS)</a> we used to represented the mobile network and network quality as a <strong>single metric that characterizes the overall performance</strong>. So as a network provider, I won&rsquo;t need to check every number on each phone call. I can aggregate it up to an NPS score as part of a region, commune, or country. This is a common approach in network profiling, but in data we don&rsquo;t have that luxury yet. Maybe we should?</p>
<p>Why am I saying all of this? Because to be able to massively ingest context, and documentation autonomously, without checking it, we need a measure. And that needs an open standard on data quality. Measures such as <code>last updated date, creator (agent/human), ratings, etc.</code> would be needed to condense a full score. If we have this, we can add a load more context, faster.</p>
<p>But right now, we do not have it, unless we use a [[data catalog]], where catalogs scan metadata and humans rate data sets etc. But with faster agents and everything autonomous, this model needs some improvements.</p>
<h2 id="and-what-the-heck-is-an-ontology">And what the Heck is an Ontology?</h2>
<p>Another term I see more frequently used and talked about, is an Ontology. I include it here, because if we talk about context, and the newly created context layer, I believe it&rsquo;s a good vehicle to explain the world for agents that need to understand not only the data our business holds, but also need to link it to common sense and how the world functions in real life. And that is what ontologies are really good at.</p>
<h3 id="ontology-broader-context-to-explain-world-models">Ontology: Broader Context to Explain World Models</h3>
<p>These are not something new. I used them back in 2017, when Airbus (where I was working at the time) <a href="https://www.airbus.com/en/newsroom/press-releases/2017-06-airbus-launches-skywise-aviations-open-data-platform" target="_blank" rel="noopener noreffer">announced Skywise</a>, an open data platform for airplane parts. And do you know what it was built on? On <a href="https://www.palantir.com/docs/foundry" target="_blank" rel="noopener noreffer">Palantir Foundry</a>, a data lake with a deep foundation in Ontologies.</p>
<p>If we go to Wikipedia, it <a href="https://en.wikipedia.org/wiki/Ontology_%28information_science%29" target="_blank" rel="noopener noreffer">defines</a> an Ontology as follows:</p>
<blockquote>
<p>In information science, an ontology <strong>encompasses a representation, formal naming, and definitions</strong> of the <strong>categories, properties, and relations</strong> between the <strong>concepts, data, or entities</strong> that pertain to one, many, or all domains of discourse.</p>
</blockquote>
<p>In simpler words, usually existing objects in the world can be described through an ontology. For example, an ontology is seen as the whole below:</p>













  
<figure><span class="img-expandable" data-full="/blog/semantic-context-layer-primer/ontology-foundry.webp">
<a target="_blank" href="/blog/semantic-context-layer-primer/ontology-foundry.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Source: <a href="https://www.palantir.com/docs/foundry/ontology/core-concepts#ontology" target="_blank" rel="noopener noreffer">Core concepts for Palantir - Docs</a></figcaption>
</figure>
<p>Palantir <a href="https://www.palantir.com/docs/foundry/ontology/core-concepts#ontology" target="_blank" rel="noopener noreffer">defines</a> it inside their tool called Foundry as:</p>
<blockquote>
<p>Ontology is a categorization of the world, <strong>it&rsquo;s the digital twin of an organization</strong>, integrating the organization&rsquo;s data and models into a coherent whole by mapping them to object types, properties, link types, and action types.</p>
</blockquote>
<p>The <code>Airport, Flight, Delay, Airline, Aircraft</code> each result in an object type, which is the <strong>schema definition</strong> of a real-world entity or event, not an ontology in itself.</p>
<p>Arrows (Departed From, Operated By, Hub For, etc.) are <strong>link types</strong> and the schema definition of a relationship between two object types, similar to how we model connections in the relational model in an Entity Relationship Diagram (ERD).</p>
<p>Things like <code>Duration, Founding Date, Range</code> are <strong>properties</strong>, which are the schema definition of a characteristic of a real-world entity or event. So the ontology is the whole schema (all five tables/datasets plus how they join together).</p>
<blockquote>
<p>[!Tip] Onto goes back to the Greek and 4th century BC<br>
If you want to get philosophical, the term onto describes efforts to understand what it means for something to exist, constructed from the Greek root &ldquo;ontos,&rdquo; meaning being, and the idea originated with Aristotle in his work on metaphysics.</p>
<p>But in terms of computer/information science, the term was first used in information systems in 1967 by Mealy, though the more significant early work came from McCarthy and Hayes in 1969, who argued that intelligent machines need metaphysically adequate representations of the world. Hayes&rsquo; 1978 paper &ldquo;Naive Physics I: Ontology for Liquids&rdquo; appears to be the first computer science paper with &ldquo;ontology&rdquo; in the title. Find more in <a href="https://arxiv.org/pdf/1810.09171" target="_blank" rel="noopener noreffer">What is an Ontology? (2018)</a></p>
</blockquote>
<h3 id="compare-semantic-with-context-layer-and-knowledge-graph-and-ontologies">Compare Semantic with Context Layer, and Knowledge Graph and Ontologies</h3>
<p>Others such as Ananth Packkildurai <a href="https://www.dataengineeringweekly.com/p/an-ontology-for-ai-agents-is-a-system" target="_blank" rel="noopener noreffer">share</a> it combined into a simple diagram where taxonomy and knowledge graph get added to showcase the technical semantic architecture leading to the context graph:</p>













  

























<figure><span class="img-expandable" data-full="/blog/semantic-context-layer-primer/technical-semantic-architecture-ai-agents.jpg">
<a target="_blank" href="/blog/semantic-context-layer-primer/technical-semantic-architecture-ai-agents.jpg" title="Source: What an Ontology for AI Agents Actually Needs">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Source: <a href="https://www.dataengineeringweekly.com/p/an-ontology-for-ai-agents-is-a-system" target="_blank" rel="noopener noreffer">What an Ontology for AI Agents Actually Needs</a></figcaption>
</figure>
<p>So another term, &ldquo;<strong>Knowledge Graph</strong>&rdquo;, simply explained, means information stored in a graph database and visualized as a graph structure, prompting the term knowledge &ldquo;graph&rdquo;. But it helps us see the semantics as the start (the definitions -&gt; can be independent of database or tooling if you have an external layer), and then external elements of the world outside of my company&rsquo;s business are described as an ontology, what Ananth calls the &ldquo;schema layer,&rdquo; since it defines the classes, properties, and rules everything else has to conform to, which, combined with a graph (the &ldquo;instance layer,&rdquo; holding the actual data), leads to the &ldquo;context graph&rdquo;.</p>
<blockquote>
<p>[!note] Other Opinions<br>
Jessica Talisman <a href="https://www.linkedin.com/posts/jmtalisman_context-is-still-a-nebulous-concept-that-share-7493443496671010816-rCJC/?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAABkA2pgBYM4xDO0z2ChYuxFhBfu4h7jp4Lo" target="_blank" rel="noopener noreffer">illustrates and says</a> it well too, that semantics is an umbrella for the vocabulary, metadata and ontology. She elaborates in great detail in <a href="https://contextandchaos.substack.com/p/ontologies-context-graphs-and-semantic" target="_blank" rel="noopener noreffer">Ontologies, Context Graphs, and Semantic Layers</a>.</p>
<p>Mehdi calls the difference between a <a href="https://motherduckdb.github.io/analytics-agent-duckdb-workshop/context/" target="_blank" rel="noopener noreffer">semantic and context</a> (page 5): &ldquo;&lsquo;Semantic&rsquo; is just a fancy word meaning there&rsquo;s a <strong>specific meaning</strong> for certain concepts and metrics in a company. Some are easy and stable; others live in people&rsquo;s heads and shift over time.&rdquo; And says that the good news is that <strong>LLMs are very good at pulling data from many sources</strong>, so we can use the full semantic context and not just a separate layer.</p>
</blockquote>
<h2 id="whats-the-take-away-graphs-and-ai-agent-integration">What&rsquo;s the Take Away? Graphs and AI Agent Integration?</h2>
<p>I know that was a lot, with a lot of definitions and potentially new terms brought together.</p>
<p>What got clearer to me, is that data <strong>gets more interconnected</strong>. When you follow the data news, as I do, many call for the context layer and connect it <strong>with a graph</strong>, because the graph is the best representation of complex interconnected knowledge, internal, external or models for describing the world, as the airport/flight example above shows.</p>
<p>Hamilton, Till, Jacob and Garret though make a counter argument in <a href="https://motherduck.com/blog/context-belongs-in-the-warehouse/" target="_blank" rel="noopener noreffer">Context belongs in the warehouse</a>, saying not to overcomplicate with sophisticated retrievals (embeddings, graphs). They showcase MotherDuck&rsquo;s new Guides work by having the agent browse a topic tree of curated guide titles and pick the right one without any keyword or vector search. This suggests that sometimes a simple index is enough.</p>
<blockquote>
<p>[!example] How do Guides work and simplify my life?<br>
Check out <a href="https://motherduck.com/docs/key-tasks/guides/" target="_blank" rel="noopener noreffer">Using MotherDuck&rsquo;s Guides to improve AI query accuracy and personalize agents</a> to help you with that, and learn more about how it works.</p>
</blockquote>
<h3 id="sophistication-is-a-cost-not-a-default">Sophistication Is a Cost, Not a Default</h3>
<p>Put those two paragraphs side by side and you get a contradiction: interconnected knowledge argues for a graph, and MotherDuck&rsquo;s own Guides argue that a flat index of titles beats embeddings and graph traversal. Both are right, they&rsquo;re just answering different questions.</p>
<p>Sophistication is a cost you should only pay if the outcome is worth much more than that cost. The airport/flight ontology earns a graph because the domain genuinely is cross-domain and interconnected: an aircraft links to a flight, a flight to an airline, an airline to a hub, and the value is in traversing those relationships. MotherDuck&rsquo;s guide index doesn&rsquo;t need that, because the actual bottleneck isn&rsquo;t relating entities to each other, it&rsquo;s picking the one right document out of a hundred, and an agent does that more reliably by scanning titles than by ranking similarity scores. A graph would have added retrieval overhead and, per MotherDuck&rsquo;s own evals, picked worse.</p>
<p>The same lens explains why a semantic layer earns a deterministic, machine-callable interface (SQL, REST, GraphQL) that a context layer doesn&rsquo;t get. It comes down to who&rsquo;s actually asking, and what it costs to be wrong. A semantic layer&rsquo;s numbers get called directly by a dashboard or a scheduled job, with no human and no LLM in the loop, so it has to be right every time, and that guarantee is worth the cost of building and maintaining it. A context layer feeds an agent that&rsquo;s still reasoning, still capable of misreading a guide or picking the wrong table, so there&rsquo;s still a checkpoint between the context and the number that ships. A context layer doesn&rsquo;t carry that weight, at least not yet: it depends entirely on your setup and your data governance. As more agent-mediated work goes straight to a dashboard with nobody reviewing it, the case for giving context layers their own consistency guarantees only gets stronger.</p>
<p>So before reaching for a graph, an ontology, or a deterministic interface, ask what the retrieval or consistency problem actually is, who consumes the result, and what a wrong answer costs. Match the tool to that answer, not to what&rsquo;s trendy this quarter.</p>
<h3 id="the-way-forward-tools-and-ai-agent-integration">The Way Forward: Tools and AI Agent Integration</h3>
<p>You might also ask, if agents like Fable are one-shotting work that used to take many iterations, what&rsquo;s actually left for humans to do?</p>
<p>There&rsquo;s already a handful of context layer tool that build a Context Layer. I have written about <a href="https://github.com/Kaelio/ktx" target="_blank" rel="noopener noreffer">ktx</a>, and am checking out <a href="https://github.com/ModernRelay/omnigraph" target="_blank" rel="noopener noreffer">OmniGraph</a>, but there are many more with <a href="https://github.com/getnao/nao" target="_blank" rel="noopener noreffer">Nao</a> or more data catalog flavored <a href="https://github.com/marmotdata/marmot" target="_blank" rel="noopener noreffer">Marmot</a>, <a href="https://github.com/open-metadata/OpenMetadata" target="_blank" rel="noopener noreffer">OpenMetadata</a> or <a href="https://github.com/datahub-project/datahub" target="_blank" rel="noopener noreffer">DataHub</a> just to name a few, and all working closely with AI agents to ease governance and management.</p>
<p>Another integration is through the ORM (Object-Relational Mapping). I once read: &ldquo;an ORM to a database is what a semantic layer is to a domain knowledge&rdquo;, so maybe we just need ORMs for agents?</p>
<p>So what&rsquo;s the way forward then?</p>
<h3 id="ai-agents-read-the-business-knowledge">AI Agents Read the Business Knowledge</h3>
<p>When we give an AI agent the business knowledge it needs through a context layer, or semantics with SQL queries and joins, it can query a warehouse correctly.</p>
<p>An agent can read a table&rsquo;s schema, but the schema won&rsquo;t reveal the inside knowledge of how &ldquo;revenue&rdquo; is defined, which tables join to which, or which columns are unreliable. A semantic layer and a context layer are two different answers to that gap and are not mutually exclusive.</p>
<p>We can see a real-world illustration of how the two layers of semantic and context can cooperate in Anthropic&rsquo;s <a href="https://claude.com/blog/how-anthropic-enables-self-service-data-analytics-with-claude" target="_blank" rel="noopener noreffer">self-service data analytics with Claude</a>. They write about how their agents are using skill instructions to leverage the semantic layer first for self-service data analytics. The skill file itself states plainly that the semantic layer is the mandatory default path for every data question, with raw SQL as the fallback used only once the semantic-layer path is shown not to cover the ask. MotherDuck Guides play a related role as curated knowledge for the agent, though MotherDuck ships the context layer first and treats a dedicated semantic layer as a separate, later question, so the routing is not the same.</p>
<h2 id="in-the-end">In the end?</h2>
<p>So, back to where I started: if a model like Fable can one-shot in minutes what used to take me many rounds with Opus, what&rsquo;s actually left for us in the data work context? Looking back at the initial semantic layer, the evolution to a context layer, and combining it with an ontology, these are not really competing tools or methodologies. They are all interconnected and work in a net. <strong>The definitions for the metrics in SQL/YAML in a semantic layer, ingestion pipeline of unstructured text and keeping it up-to-date with context layers, and making sense of the world for the agents with ontologies</strong>.</p>
<p>And most of what we have evolved to, is mostly due to the sheer power and advancement of agentic work, that agents can autonomously keep up with metadata and data we want to convert into context. But at the same time, it always <strong>defaults back to the fundamental question of what my data governance</strong> looks like, and what my overall data architecture is to achieve the goal at hand.</p>
<p>And as a16z&rsquo;s three-part context layer implied, as well as Anthropic&rsquo;s semantic-layer-first skill: <strong>someone still has to decide what&rsquo;s true</strong>. We still need the human in the loop, especially with data. And that&rsquo;s the whole crux of Amdahls Law, as long as a person or a team needs to decide or gets involved, we lose the overall speed, but I think that&rsquo;s the price we still pay, if we want good, verified data quality in our enterprise data warehouse or analytics platforms.</p>
<p>And as I raised with the metric to measure data quality, which we do not have, it confirms another true statement, that the hard part in data is never building semantics or the context once, it&rsquo;s how and who keeps it <em>correct</em> and <em>maintained</em>. That&rsquo;s the answer to my opening question: not a tool, but judgment, the same judgment behind the &ldquo;human refinement&rdquo; step a16z pointed to, and the same one behind deciding whether a problem is worth a graph or is better served by a flat index.</p>
<p>Large language models are getting exceptionally good at writing the SQL with the right context, and at maintaining just that. But most often, deciding what &ldquo;revenue&rdquo; means still requires deep company-specific knowledge (for now).</p>
<hr>
<p>Listen more on these very related discussed topics with the already teased Jacob&rsquo;s podcast about <a href="https://analyticshour.io/2026/06/23/300-are-semantic-layers-really-necessary/" target="_blank" rel="noopener noreffer">Are Semantic Layers Really Necessary?</a>. Or check out the great example of how AI agents can help with creating a semantic layer using the open source semantic layer Malloy at <a href="https://motherduck.com/blog/AI-writes-the-semanrites-the-semantic-layer/" target="_blank" rel="noopener noreffer">AI Writes the Semantic Layer | MotherDuck</a>.</p>
<hr>
<pre class=""><em>Full article published at <a href="https://motherduck.com/blog/context-layer-vs-semantic-layer-ontology/" target="_blank" rel="noopener noreferrer">MotherDuck.com</a> - written as part of <a href="/services/">my services</a></em></pre>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>They call it a &ldquo;modern&rdquo; context layer. I&rsquo;m not sure we already went through the evolution of context layers, so calling it modern does not add much value. To me it&rsquo;s the first version of a context layer.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>If You Always Enjoy It, You&#39;re Not Pushing It Hard Enough—Benn Stancil (Show Your Workflow #1)</title>
    <link>https://www.ssp.sh/blog/benn-stancil-on-deadlines-and-storytelling/</link>
    <pubDate>Fri, 11 Sep 2026 07:00:00 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/benn-stancil-on-deadlines-and-storytelling/</guid><enclosure url="https://www.ssp.sh/blog/benn-stancil-on-deadlines-and-storytelling/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>I had the pleasure to discuss and analyze the workflow of none other than <a href="https://www.linkedin.com/in/benn-stancil" target="_blank" rel="noopener noreffer">Benn Stancil</a>, legendary writer and storyteller in the data space and beyond, who started writing back in 2013 (with a seven-or-eight-year startup break in between). This is the first interview in the series «<a href="/show-your-workflow/" rel="">Show your Workflow</a>», and I couldn&rsquo;t be happier to start with Benn.</p>
<p>As this series is all about how writers write, how to come up with stories, how to take notes (or not), and each one&rsquo;s workflow, I asked Benn about the origin of his writing and how it all started with his weekly Friday &ldquo;<a href="https://benn.substack.com/" target="_blank" rel="noopener noreffer">let&rsquo;s fight articles</a>&rdquo;, how he uses deadlines to push him beyond perfect, how he processes ideas, and how he mastered the art of storytelling with analogies he learned from his talks he gave, converting it into his writing style and voice. We talk about how he uses Sublime Text and Google Docs, and end with approaches on how to start today.</p>
<p>This episode is filled with insights to get better at writing. I hope you enjoy it as much as I did when talking to Benn.</p>
<blockquote>
<p>[!abstract] Who is Benn Stancil: An Introduction<br>
<a href="https://www.linkedin.com/in/benn-stancil/" target="_blank" rel="noopener noreffer">Benn Stancil</a> is mostly known as the founder of <a href="https://mode.com/" target="_blank" rel="noopener noreffer">Mode</a> (a modern BI tool), where he started the company blog and became known as one of the most prolific writers in data. He co-founded Mode in 2013 and worked there until 2024, after it got acquired by ThoughtSpot in 2023. Since then he has been writing weekly on his <a href="https://benn.substack.com/" target="_blank" rel="noopener noreffer">own Substack</a>.</p>
</blockquote>
<h2 id="how-benn-got-into-writing">How Benn Got into Writing</h2>
<p>Starting at the beginning of how Benn started writing, he told me that it started by accident, as it so often does. When he started a company called Mode in 2013, there were three of them, and he didn&rsquo;t build or lead the company as a CEO, so he started writing the company blog posts. That was &ldquo;pre content marketing&rdquo;, he mentions, before there was &ldquo;the obvious playbook of go be an influencer on linkedin&rdquo;, and he started writing about topics that interested him. It wasn&rsquo;t about Mode, but about the data space—also because there was no product to write about in the early beginning, he said 😉.</p>
<p>He noticed that people started to like it. After a while though, he was more involved in daily work and building a startup, which meant he didn&rsquo;t have time to write too much anymore. He &ldquo;always kind of had this notion that I would get back to doing writing more at some point&rdquo;.</p>
<p>Much later, around 2021, when he did get back to it, his blog that he started had evolved into something different, &ldquo;very much just classic company blog&rdquo;. Much later, around 2021, when he did get back to it, the original Mode blog had evolved into something different, &ldquo;very much just classic company blog&rdquo;, so they made a decision to start a new one <a href="https://benn.substack.com/" target="_blank" rel="noopener noreffer">Substack</a>. Not really deliberately, but because that was the thing you used, and with no fixed plan—the very first posts were still data-team-ish, and it evolved from there:</p>
<blockquote>
<p>The first posts that were kind of data posts were a little bit more like things you learn at work, it was a little more like data teamy stuff. But at some point it just sort of evolved into whatever it became.</p>
</blockquote>
<p>Now he writes about general topics, what he wants to write about or what he experienced during the week.</p>
<h3 id="if-you-dont-hate-it-sometimes-you-dont-push-enough">If You Don&rsquo;t Hate It Sometimes, You Don&rsquo;t Push Enough</h3>
<p>He mentioned that, back in college and earlier, he didn’t particularly enjoy writing, and was more drawn to math and other things. So I asked whether more than a decade of writing had changed that:</p>
<blockquote>
<p>I mean, I don&rsquo;t know. You like it sometimes and <strong>you hate it sometimes</strong> [&hellip;] and <strong>there are days you&rsquo;re like it&rsquo;s fun</strong>.</p>
</blockquote>
<p>Writing is not like a regular job:</p>
<blockquote>
<p>It&rsquo;s not something you&rsquo;re like: great, I&rsquo;m going to <strong>sit down at nine and work until five</strong>, and I have my nine to five that I just do it. It&rsquo;s like you&rsquo;re not doing that, because it doesn&rsquo;t come out that way.</p>
</blockquote>
<p>He also doesn&rsquo;t want to call it a job, as currently, it isn&rsquo;t his job to write. He does it because he wants to, but he said something interesting that I believe is so true:</p>
<blockquote>
<p><strong>If you always enjoy it, you&rsquo;re not pushing it hard enough.</strong> Like, if you love playing basketball and you&rsquo;re good at it, and all you do is you&rsquo;re like: I will play to the point where I no longer just love playing it, then you&rsquo;re not actually pushing yourself very hard. You have to get to a point where you kind of hate it. That doesn&rsquo;t mean you&rsquo;re gonna hate it always, doesn&rsquo;t mean you&rsquo;re gonna resent it, it doesn&rsquo;t mean you&rsquo;re gonna walk away from this thing being like: oh my god, I can&rsquo;t do this anymore. But I think to make something good, you have to hate it sometimes.</p>
</blockquote>
<p>As an example of basketball:</p>
<blockquote>
<p>I&rsquo;m sure LeBron loves playing basketball, I&rsquo;m sure various great writers that we all like love writing things [&hellip;] I&rsquo;m sure there are days they hate it, and I think they have to. I think that&rsquo;s why they&rsquo;re the best ones.</p>
</blockquote>
<h3 id="having-a-schedule-a-deadline-is-the-most-important-tool">Having a Schedule, a Deadline, Is the Most Important Tool</h3>
<p>On that note of &ldquo;whatever it became&rdquo;, I asked him how he got his schedule fixed (he posts every Friday), and how he found his writing voice.</p>
<p>Benn said clearly that deadlines are <strong>something useful to have</strong>. It&rsquo;s the thing that gets the thing done. Later he added, it&rsquo;s even more important when you start out. You might think that you don&rsquo;t have something interesting to say, and you never feel ready to publish. That&rsquo;s why you need to just ship it a handful of times: you need to have some standard of quality and expectations, but otherwise, just ship.</p>
<p>Many people have this:</p>
<blockquote>
<p>They have an idea and it&rsquo;s not sort of fully baked, and because they have no real deadline or timeline to hit, it always feels like it&rsquo;s not quite ready [&hellip;] it&rsquo;s not ready to be released yet.</p>
</blockquote>
<p>In the end, he thinks, you can&rsquo;t just wait until it&rsquo;s perfect.</p>
<blockquote>
<p>[!tip] His deadline arrived from a joke, &ldquo;It&rsquo;s Friday, let&rsquo;s fight&rdquo;<br>
After he started his own Substack, he once posted <a href="https://benn.substack.com/p/the-case-against-sql-formatting" target="_blank" rel="noopener noreffer">The case against SQL formatting</a>. The whole post was a little bit of a joke, he said. Because of that, he <a href="https://x.com/bennstancil/status/1403368021872320519" target="_blank" rel="noopener noreffer">tweeted</a> it on Twitter as a joke: &ldquo;it&rsquo;s friday let&rsquo;s fight&rdquo;, not really a serious thing, but that we can fight about SQL formatting (a thing data people do 😉).</p>
<p>![[tweet-started-it-all.png]]</p>
<p><a href="https://benn.substack.com/p/the-more-the-merrier#footnote-2" target="_blank" rel="noopener noreffer">Someone at the company</a> kind of liked that Friday fun fight angle, and it also fit Benn a little bit as a character, a less serious thing, talking about a specific topic. And then it just became kind of an accidental thing, and he started posting stuff on Fridays, and at some point, that was kind of his deadline for the Friday fights.</p>
</blockquote>
<h2 id="benns-process-of-idea-creation">Benn&rsquo;s Process of Idea Creation</h2>
<p>When I asked Benn how he comes up with these refreshing and interesting new ideas he has every Friday, he said the <strong>ideas roll around with him during the week</strong>. What&rsquo;s sometimes the hardest part is turning them into a story (more on that below).</p>
<p>Usually he takes them with him and they are very messy:</p>
<blockquote>
<p>[&hellip;] you have <strong>some working things in your head</strong> that are <strong>usually a mess</strong>. You have to sort of run around in the world with those things in your head and they run into things. It&rsquo;s like you will find all these connections, because you have something sort of in the back of your mind, it will suddenly <strong>connect to a bunch of things</strong> [&hellip;] that are <strong>not obvious connections</strong>.</p>
</blockquote>
<p>And so ideas get formed really that way:</p>
<blockquote>
<p>It&rsquo;s a thing that exists that you sort of roll around in the world, and it picks up little pieces as it goes, and <strong>eventually something kind of interesting comes out</strong> of that.</p>
</blockquote>
<h3 id="random-strings-pulled-together">Random Strings Pulled Together</h3>
<p>Another speciality of Benn is to have lots of links to random things as part of his blogs. Some hate it, others love it (I love them!). He said something interesting, as many ask him if he adds them at the end. He says no. It&rsquo;s all part of the writing process.</p>
<p>He might think of a song, search for it on YouTube, and watch it. While watching, he gets a completely new emotion, or idea, that will fit back to the topic. And sometimes, these short distractions or links that come to mind are influencing the story. So besides being added while the idea evolved, they <strong>even influence the storyline itself</strong>.</p>
<h2 id="the-art-of-storytelling-of-benn">The Art of Storytelling of Benn</h2>
<p>If you haven&rsquo;t read Benn&rsquo;s articles, you are missing some of the best storytelling in the whole data space, and beyond. Once he has come up with an idea, added links, and talked with people about it, he <strong>finds analogies in real life</strong>.</p>
<p>The <strong>storytelling is the hardest part</strong>, he says. The worst is when he has a lot of ideas with &ldquo;here&rsquo;s a lot of notes&rdquo;, but no story, no connected thread. Then it takes him much longer, and he might not find a story to tell.</p>
<p>Some other times though, the story <strong>emerges naturally</strong>, and it&rsquo;s a point of relief. It&rsquo;s always different though. Sometimes he has the beginning he really likes, which carries the point. Or other times he has a paragraph that is fixed and great, and then he finds the story around it.</p>
<h3 id="benns-trick-to-tell-a-good-story-finding-or-setting-up-an-analogy">Benn&rsquo;s Trick to Tell a Good Story: Finding or Setting Up an Analogy</h3>
<p>A great story for Benn is having a great analogy. Finding a good intro is really, really hard, he says.</p>
<p>Then he kind of &ldquo;cheats&rdquo;: he finds an analogy from Batman, basketball, or whatever comes to mind, and the analogy starts to drive the whole point he wanted to make.</p>
<p>This <strong>way of storytelling emerged for him from the many presentations he did</strong>, and swapped over to writing. In his decks there is a talk track, the thing he is saying, and the slides are a kind of background commentary: a point that is analogous to Batman simply gets a slide of Batman next to it. And once one piece of the analogy fits, &ldquo;you tacked the points onto the analogy rather than the analogy onto the points&rdquo;—which is what <strong>makes it look so tight and clever</strong>.</p>
<blockquote>
<p>[!note] This reminds me of Derek Sivers<br>
This reminded me of Derek Sivers too: when you have writer&rsquo;s block, just add [[constraints]]. In music, say you want to play with only two chords, or only two instruments, and suddenly your creativity is flourishing. You have clear boundaries you can play with, instead of having an open end, where everything is possible, and it is very hard for the mind to start or finish anything. I think that&rsquo;s a little bit what&rsquo;s happening here too: if you start with Batman, the figure, you start with a constraint.</p>
</blockquote>
<p>Some say, when they read, &ldquo;get to the point&rdquo;, because he writes so indirectly through an analogy, but for him, &ldquo;there&rsquo;s no fun in that&rdquo;, and <strong>he&rsquo;s &ldquo;not trying to make an argument in court&rdquo;</strong>.</p>
<p>Also because he has a deadline on Friday, he needs to get it done every Friday, and that&rsquo;s where the mood influences the piece as well. I agree, and I call it [[Writing from The Heart|Write from the Heart]]: if you feel angry or emotional, that is felt in the writing, and it can give the piece the needed elevation or realness.</p>
<h2 id="workflow-the-process-the-note-taking">Workflow: The Process, The Note-taking</h2>
<p>This series is also about behind the scenes, about tooling, text editors and workflow each uses. We discuss Benn&rsquo;s process and what he uses below.</p>
<h3 id="the-writing-tools-google-docs-and-sublime">The Writing Tools: Google Docs and Sublime</h3>
<p>He typically starts by just writing ideas in <a href="https://www.sublimetext.com/" target="_blank" rel="noopener noreffer">Sublime Text</a>. He likes it: it&rsquo;s distraction-free, undecorated, so he can just focus on the text at hand, and write without formatting, grammar, or errors in mind. Once there&rsquo;s a bit more material, he moves it into <strong>Google Docs</strong> and works through it there, adding ideas, paragraphs, and links, with some docs staying open (and growing to 10 pages) for a very long time.</p>
<p>He just starts writing, whatever comes to mind or happens: bullet points, conversations, and paragraphs.</p>
<h4 id="the-publishing-workflow">The Publishing Workflow</h4>
<p>Ideally he has it completely done in Google Docs and just copies it over to Substack, but usually he still proofreads it a couple of times there and fixes small last-minute things. He doesn&rsquo;t use the Substack editor for anything material.</p>
<h3 id="why-not-obsidian">Why Not Obsidian?</h3>
<p>I asked Benn why he didn&rsquo;t use Obsidian as it seems that he is big on connections, and that they are a big part of his writing style. He said that Google Docs is good enough. He tried Obsidian-typish notes with connections, but it never stuck.</p>
<p>He even tried to vibe-code such a thing himself, something that &ldquo;reads your writings and can kind of present you related ideas as you do it&rdquo; (sounds similar to <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f" target="_blank" rel="noopener noreffer">Karpathy LLM Wiki</a>), but usually it turned out to be &ldquo;more of a distraction&rdquo; than he wanted it to be, and on top of that, he&rsquo;s &ldquo;not convinced that there isn&rsquo;t a good way to do that somehow&rdquo;.</p>
<p>He also has &ldquo;a very unproven theory&rdquo; for why this kind of <strong>connection-finding is exactly where AI struggles</strong>:</p>
<blockquote>
<p>The things that are often most interesting are disparate connections between things that don&rsquo;t obviously have a connection (more on that below).</p>
</blockquote>
<p>He instead has this kind of &ldquo;<strong>serendipity type way</strong>&rdquo;, &ldquo;where you run into various things around the world&rdquo;. In a way, it&rsquo;s applying the Obsidian-type notes more to real life.</p>
<p>Also organization-wise, his ideas are all over the place, rather than in one note-app:</p>
<blockquote>
<p>Currently on my <strong>desktop</strong> I have two lists of blog post notes and a <strong>Sublime thing</strong>, there is a list in an <strong>Excel sheet somewhere</strong>, a <strong>Google Sheet somewhere</strong>, there is obviously a <strong>bunch of half-written drafts</strong> and <strong>Google Docs</strong>.</p>
</blockquote>
<h3 id="why-benn-does-not-use-ai-tools-for-writing">Why Benn Does Not Use AI Tools for Writing</h3>
<p>When asked how he uses AI as a tool, he said he does not use Grammarly, and he doesn&rsquo;t give posts to Claude or ChatGPT for edits. The one exception: Google Docs&rsquo; built-in AI-generated grammar suggestions (the blue squigglies), those he does use. But what he uses a lot is ChatGPT for research, as an enhanced Google to explain a thing in history, or why it happens.</p>
<p>The other big use case is for <a href="https://www.thesaurus.com/" target="_blank" rel="noopener noreffer">Thesaurus</a> kind of questions: &ldquo;I am trying to figure out a word to describe X&rdquo;, where you &ldquo;can give it two words and be like: something like this&rdquo;.</p>
<p>He is very intentional about not using Grammarly, because of the way <strong>AI wants to compress the writing</strong>. He said GenAI generally tries to make the writing tight. It hates meandering, but Benn likes meandering.</p>
<p>He already hated compressing before AI. E.g., when they collected ten important statements about the company that they came up with during a brainstorming session with the team, the sticky notes sessions, and then tried to compress them into 1-2 sentences of a mission statement. In the end, the outcome was always to his dissatisfaction.</p>
<h2 id="where-does-benn-get-his-inspiration-from">Where Does Benn Get His Inspiration From?</h2>
<p>I asked him where his inspiration came from, and if he believes that to write &ldquo;interesting things&rdquo;, you have an interesting life<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Would you agree?</p>
<p>He thinks you need to be exposed to &ldquo;stuff&rdquo;. His <strong>initial ideas come from conversations, being out there</strong>. He says you can&rsquo;t start with the objective of writing to generate &ldquo;lead flow for potential investments&rdquo;. He told me about a VC he knows who writes infrequently, but when he does, people care. That VC gets a bunch of people reaching out asking how to write a blog like that for lead flow. And his (and Benn&rsquo;s) take is:</p>
<blockquote>
<p>If you&rsquo;re starting from the objective of: the point of this is to create leads, or the point of this is to generate people clicking on my company website [&hellip;] then you sort of already lost the plot [&hellip;], just write interesting things on its own.</p>
</blockquote>
<p>Interestingly, Benn thinks people:</p>
<blockquote>
<p>Slightly overrate the point of having a point.</p>
</blockquote>
<p>You don&rsquo;t have to say something that&rsquo;s never been said before, you just have to <strong>tell it in an interesting way</strong>. And, Benn also said, it must be fun (sometimes, as we heard above).</p>
<h3 id="benns-very-unproven-theory-for-why-ai-struggles-with-writing">Benn&rsquo;s Very Unproven Theory for Why AI Struggles with Writing</h3>
<p>He makes the example of the board game <a href="https://en.wikipedia.org/wiki/Codenames_%5c%28board_game%5c%29" target="_blank" rel="noopener noreffer">Codenames</a>: you give your team a one-word clue and a number, and they have to find the cards it links. &ldquo;Hogwarts, four&rdquo; obviously covers <em>magic</em> and <em>school</em>, but it might also cover <em>train</em>, because of the Hogwarts Express. The point is that the words are</p>
<blockquote>
<p>associated in a weird way [&hellip;] you wouldn&rsquo;t necessarily associate magic and train.</p>
</blockquote>
<p>That, he thinks, is roughly how creativity works in general:</p>
<blockquote>
<p>You have exposure to some particular thing and then you have some idea, like whatever business thing you care about, and you make those connections. [&hellip;] You want to talk about data stuff, but you also know a lot about, I don&rsquo;t know, camping. You find this connection between these two things, where these things are not at all related, but it tells an interesting story.</p>
</blockquote>
<p>Every human has a &ldquo;relatively sparse knowledge of the world&rdquo;, and Benn says this is a lot of <strong>what creativity is</strong>:</p>
<blockquote>
<p>People smashing together very strange things, because their experiences are like this eclectic collection of things that aren&rsquo;t obviously all tied together.</p>
</blockquote>
<blockquote>
<p>[!note] The Opposite way, of &ldquo;saying something that sounds smart&rdquo;<br>
&ldquo;Making a smart point&rdquo; or &ldquo;saying something smart&rdquo; didn&rsquo;t work for him, because you can&rsquo;t see these interesting connections between things that don&rsquo;t obviously exist. So having an interesting life is helpful for that.</p>
</blockquote>
<h3 id="finding-your-writing-voice">Finding Your Writing Voice</h3>
<p>In that regard, as finding your writing voice is inevitable and important for you as a writer, I asked how he found his &ldquo;Writing Voice&rdquo;.</p>
<p>Benn said it just emerges: &ldquo;you probably don&rsquo;t go in being like, this is the voice I want to have, and now you do it&rdquo;. His voice emerged in Slack channels, where he wrote paragraphs upon paragraphs. He didn&rsquo;t say it exactly like that 😉, but these were essentially reps of just him writing to inform his peers at work. Doing that often, you might find ways that work better, and things that don&rsquo;t.</p>
<h4 id="dont-strip-out-too-much-of-the-story">Don&rsquo;t Strip Out Too Much of the Story</h4>
<p>He also made an interesting point, one that a lot of writers face: if you strip out too much of the story and your voice, all you&rsquo;re left with is having to say something that is, by itself and in plain language, original, and &ldquo;it&rsquo;s really hard to say original stuff&rdquo;.</p>
<p>Reduce a novel to a bullet list of plot points and those points are pretty unoriginal, because &ldquo;there aren&rsquo;t that many stories you can tell&rdquo;. <strong>The interest comes from how you tell it</strong>.</p>
<p>Same with databases: the bare bullets are hard and dry, and that&rsquo;s where his writing style with analogies and making a story really works.</p>
<h2 id="what-approach-would-you-suggest-to-someone-starting-out-today">What Approach Would You Suggest to Someone Starting Out Today?</h2>
<p>Before we wrapped up, I asked him one last question, about what he would suggest for someone starting to write today.</p>
<p>He gave three clear pointers:</p>
<ol>
<li>Have deadlines, which is critical especially early on</li>
<li>Your motivation has to be pure. You kind of need to want to do it (like a musician who likes to play the instrument). Otherwise, do something else. And keep your ego out of it. Readers come for the ideas, not for you: &ldquo;<strong>you&rsquo;re not always the main character</strong>&rdquo;.</li>
<li>And <strong>the writing is not the bullet points</strong>, it&rsquo;s filling things in between, <strong>filling in the text between the bullet points</strong>. Like the stand-up Slack messages he had to write to the team, he enjoyed them.</li>
</ol>
<p>I hope you enjoyed this typed-out interview with Benn Stancil as much as I did learning from him in our conversations. Benn is an exceptional writer, and seeing his process and workflow of how he writes is pure joy to me. I hope I could bring some of this joy over, and that, if you are a writer, you could learn something.</p>
<p>Please follow him on all his <a href="https://benn.website/" target="_blank" rel="noopener noreffer">socials</a>, and subscribe to his Friday Fights at <a href="https://benn.substack.com/" target="_blank" rel="noopener noreffer">Benn&rsquo;s Substack</a>. Also check out his latest article and learn more about <a href="https://benn.substack.com/p/how-to-have-ideas" target="_blank" rel="noopener noreffer">How to have ideas</a>, or <a href="https://benn.substack.com/p/how-to-find-insights" target="_blank" rel="noopener noreffer">How to find insights</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Andrew Sean Greer says on <a href="https://youtu.be/0kkAl04_0og" target="_blank" rel="noopener noreffer">Write Better Stories in 72 Minutes</a>: &ldquo;highly recommend[s] life experiences,&rdquo; ideally through an interesting day job.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>Writing really is an Emotional Rollercoaster</title>
    <link>https://www.ssp.sh/blog/writing-since-2015/</link>
    <pubDate>Fri, 28 Aug 2026 11:18:22 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/writing-since-2015/</guid><enclosure url="https://www.ssp.sh/blog/writing-since-2015/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>[[Writing]] has become my passion over time; I have built a whole company based on technical writing services (even though most say that&rsquo;s impossible). After doing it for 11 years publicly (much longer privately), I still enjoy it every day. If I could have one dream, I&rsquo;d probably imagine the book-author lifestyle, where I go to a faraway place or island and just write in my journal, articles from my vast notes system (<a href="/brain/everything-ive-published-came-out-of-one-obsidian-vault/" rel="">~3 million words</a>, or 2-3 books.</p>
<p>But also, I wanted to have a medium to express [[My Terminal Workflow with MacOS and Linux|my workflow]], <strong>discuss how others write</strong>, and learn even more. It&rsquo;s truly an art form and a craft to write, at least it is to me. It&rsquo;s not something you just sit down and do for 5 hours (as my upcoming guest liked to say). It&rsquo;s essentially your life. You observe, you find things to write about, you get angry, you find a cool hack, you create a story. It&rsquo;s all of it! And you can&rsquo;t plan it.</p>
<p>Some days you write 2-3 articles/notes in parallel; other weeks you write none at all. And then the imposter syndrome. Everything you write is public. <strong>My writing process is usually</strong>:</p>
<ol>
<li>Ohh this is so great, this is so interesting, I&rsquo;m looking forward to sharing</li>
<li>Next day: oh what&rsquo;s that, this is crap, why should I publish this?</li>
<li>Then the loss of motivation: the hard part. When you need to make it work, you change, but it&rsquo;s not right. You change again, you get feedback, you change. And eventually you see the light and can say, OK, yes, this is good!</li>
</ol>
<p><strong>Writing really is an emotional rollercoaster</strong>, and it&rsquo;s been that way with all the articles I have written. Less so with the notes on my second brain, there I&rsquo;m less &ldquo;storytelling&rdquo;, but more like &ldquo;curating facts&rdquo;.</p>
<p>So long story short, yes, this is what my new show is all about: «<a href="/show-your-workflow/" rel="">Show Your Workflow</a>». I hope you like it. Subscribe if you haven&rsquo;t already to get the next one, if that is something that interests you.</p>
<hr>
<p>Above written is the writer flow at a high level, and how I experience it most of the time. Here I share writing process over time as I have documented that over time. Each part is similar in a way, but also holds some different key pieces and determined how I wrote at that time. These are rather raw [[journaling|journals]], but maybe interesting to see.</p>
<h2 id="my-writing-process-as-of-2025-10-23">My writing process as of 2025-10-23</h2>
<p>I start thinking, I write, outline, and, very importantly, I stop. I sleep, I go into [[nature]], I just let it sit.</p>
<p>After coming back, I write some more. Then again, I talk to people and anyone about the topic, and I brainstorm with AI. And I write some more.</p>
<p>All happens in markdown ([[Obsidian]] for me), and I&rsquo;m <strong>constantly</strong> changing titles, adding new ones, and <strong>reorganizing</strong>.</p>
<p>The flow does feel off. I start restructuring again. The key point for me is that when I begin merging related topics, sometimes similar, and putting the essential message further up.</p>
<p>Sometimes I write an intro, add some context, and include some relevant info. I&rsquo;m adding more insights. And the most important one that I wanted to talk about is very far down.</p>
<p>Now that I&rsquo;m at the point where the main content will be naturally moved up, I&rsquo;m deleting or removing content. This is when it will start to feel cohesive. The reading flow starts to make sense. And from there, I just keep putting it together, making the reading flow perfectly.</p>
<p>Each chapter already has tons of notes, links, and insights, so finishing a first draft from here is usually easy and exciting.</p>
<p>Once I have a draft, I fix grammar with Claude Code and get feedback now, requesting very high-level feedback. Before I do another major rework, bring a great first draft. Go over 3-5 more times. I will notice how my changes are getting smaller and smaller until I know deep in my [[Gut Feeling|gut]] it&rsquo;s ready.</p>
<h3 id="a-trick-i-learnedabout-deleting-writing">A trick I learned—about deleting writing</h3>
<p>It was always <strong>hard for me to cut out my hard-earned content</strong>. So I discovered a trick.</p>
<p>By just adding a <code># Take Out</code> chapter at the end of each article, it tricks my brain into thinking: &ldquo;it&rsquo;s not deleted&rdquo;, &ldquo;I can get it back&rdquo;, and this way it&rsquo;s much easier to take out writing than to delete hard-earned hours on a paragraph.</p>
<h2 id="my-writing-from-as-of-2025-09-17">My Writing from as of 2025-09-17</h2>
<p>My process of writing is in two phases and the [[My Distraction-Free Typewriter (Micro Journal)|distraction-free typewriter]] helps me with the writing phase a lot in the first writing phase:</p>
<h3 id="1-outlining">1. Outlining</h3>
<ul>
<li>this is just exploring, listing (!! this is key)</li>
<li>and write anything that comes to mind, no matter how good, or the other it fits in</li>
<li>then I have my [[Second Brain]] with ExcaliBrain and Graph view with backlinks that give me interesting insight which notes might link or are related. I like Graph Analysis (Today [[Obsidian Smart Connections]]). This will give me many related ideas that the plugin finds via embedding, locally, without AI. Gives me <strong>ideas through the connections that I otherwise wouldn&rsquo;t</strong> have.</li>
<li>All of this usually happens on my laptop with [[Obsidian]].</li>
</ul>
<h3 id="2-writing-phase">2. Writing phase</h3>
<ul>
<li>Nowadays, as I have the [[My Distraction-Free Typewriter (Micro Journal)]] device, I will commit that state to the private GitHub repo, and pull it on my micro journal. After that, I&rsquo;m offline, only with these ideas and my backlinks, and I&rsquo;m trying to figure it out.
<ul>
<li>The Micro Journal has allowed me to think more, take a break, and focus for hours on writing, instead of constant distraction writing at the computer</li>
</ul>
</li>
<li>A key here is also the [[Markdown]] format: I can simply move stuff around without losing formatting or anything else.</li>
<li>sometimes I copy existing paragraphs from my [[Second Brain]] that I have written before.</li>
</ul>
<h3 id="finalizing">Finalizing</h3>
<ol start="3">
<li>After a while, I sit <strong>back and review</strong>.</li>
<li><strong>Write more</strong>&hellip;</li>
<li><strong>Get a mental breakdown</strong> as I think all I have written is so bad.</li>
<li>Next day, change the flow, use AI to help me with the reading structure, maybe heading names.</li>
<li>Finalize, edit, grammar check.</li>
<li><strong>Publish</strong>.</li>
</ol>
<p>That&rsquo;s my workflow for writing an article. There are other [[Type of Notes]] that can be written and other [[Type of Notetakers]]. Which might help to organize your knowledge.</p>
<p>I think it&rsquo;s important to have a place where you store your knowledge (notes from books, insights you read online, ideas, etc.) that can help you when you write too.</p>
<h2 id="my-writing-process-as-of-2022-08-19">My Writing Process as of 2022-08-19</h2>
<p>Write at least two crappy pages a day. The writing is in fact re-writing. A <strong>process of reviewing</strong>:</p>
<ul>
<li>first-round review for yourself, what you like</li>
<li>the second one do it for your fans</li>
<li>third for your critics, what they might come up with (bad) and improve</li>
</ul>
<p>Then <strong>questions / proof-reading</strong>:</p>
<ul>
<li>look for confusing stuff, that should not be in there (if unsure or in doubt: take it out!)</li>
<li>What are 10% that you would cut if you had to? Or what are 10% that you will keep for sure</li>
</ul>
<p>And again, the [[Writing is Thinking]]!</p>
<p>I also like to write about things I don&rsquo;t know, like Morgan Housel <a href="https://www.youtube.com/watch?v=C02Jy2pCZz0" target="_blank" rel="noopener noreffer">discussed</a> in his interview with David Perell. It&rsquo;s <strong>following my curiosity</strong>. First, I will learn something ([[Learn for Life]]), I will get [[Clarity]], and it&rsquo;s interesting. This kind of flow or fun to write, will translate to the reader too.<br>
according to David: <strong>laughter is the sound of comprehension</strong>. It&rsquo;s when you get an idea, when it clicks or you make a connection. Happens to me when I find new insight while writing the second brain, I&rsquo;ll smile.</p>
<h2 id="my-writing-process-initially-documented">My writing process initially documented</h2>
<p>I always need to remind myself:</p>
<blockquote>
<p>[[Writing is hard]]. It&rsquo;s easy to start, hard to finish. You get stuck, you have a blockage, your creativity is low, you get interrupted, and you lose confidence in your writing. Reminding myself that the first 10 minutes are the hardest and envisioning the end product keeps me motivated.</p>
</blockquote>
<h3 id="writing-phase">Writing Phase</h3>
<p>This is what brought me to writing. To release my thoughts ([[Brain Dump]]), make my brain accessible for more, and be creative. I like to brainstorm, change, delete, add, research, and do the thinking while writing. When writing on a computer, I can do it almost at the speed of my thinking; I do not lose thoughts as I would when writing on paper ([[Digital vs Paper]]). I can stop at any given time and come back, as all the thinking is written down. As well, I love the editing part. Making it better, finding correlations, and things I wouldn&rsquo;t have expected before.</p>
<p>But before getting into the editing phase, during the initial phase, when drafting or coming up with the content itself, I try to get into distraction-free [[Deep Work|Flow]]. Long chunks of uninterrupted time, Cal Newport talks about [[Deep Work]] in his book.</p>
<h3 id="editing-phase">Editing Phase</h3>
<p>Usually, editing is when I learn the most, but at the same time, it&rsquo;s also the most challenging step. I will get into a [[Writers Block]] and want to start a new thing instead. But by switching between [[Creativity vs Productivity]] or just going into nature with my thoughts and nothing else, usually one of them helps. Or if not, I leave it for a couple of days, weeks, or months and come back another time. Keeping the [[Ultradian Rhythm]] in mind also helps overcome the initial hardest ten minutes.</p>
<p>When I let the writing stay for a while, that typically leads to better quality because when I come back, I have new insight and might delete or change existing thoughts. The longer the wait, the more insightful that note will get. Therefore, I prefer to keep it slow. In the end, the reader will not mind if you spend one day or three months on an article; he appreciates it if the <strong>reader flow</strong> is good and he can learn. At least that is my goal as a writer: to give readers some learning.</p>
<p>Writing doesn&rsquo;t always need to be about an article. You can just put some thought, [[Journaling]], or something you learned or find interesting, into a note. Save it for later. You mostly don&rsquo;t need the thing you just knew at that moment. Something you find interesting today is doubtful to be of value right now. But maybe, in two years from now.</p>
<blockquote>
<p>[!quote] David Perell</p>
<p>Writing is so painful when you write about something you do not care about and so blissful when you write with your [[Principles]] aligned. Writing from a blank page reveals the true things you deeply care about. <a href="https://youtu.be/aIWDvuJhVm8?si=r_EEzGIMrxsqslPU" target="_blank" rel="noopener noreffer">YouTube</a> with Paul Millerd</p>
</blockquote>
<p><em>This last initial process was is shared as part of <a href="/brain/writing/#writing-process" rel="">On Writing</a> note.</em></p>
]]></description>
</item>
<item>
    <title>Figma for Agents: How Airflow&#39;s Creator Coordinates AI ft. Maxime Beauchemin</title>
    <link>https://www.ssp.sh/blog/figma-for-agents-max-beauchemin/</link>
    <pubDate>Thu, 06 Aug 2026 09:40:08 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/figma-for-agents-max-beauchemin/</guid><enclosure url="https://www.ssp.sh/blog/figma-for-agents-max-beauchemin/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>It&rsquo;s hard to keep up with the AI evolution; new AI tools drop every week, but how are experienced practitioners actually using them? Most of us are overwhelmed and unsure about the many possibilities, yet we need to keep going and do our work. You might use AI agents all day long, parallelize them with AI Orchestrators, tmux, git worktree, and so on, using AI IDEs, but in the end, you still need to coordinate and understand what the agents produced, potentially test it, which makes it even harder to keep up.</p>
<p>Luckily, Maxime Beauchemin, the creator of Airflow and Superset and the person who defined what &ldquo;data engineer&rdquo; meant for a decade (more on him below), joins us to show how he uses agents and what he&rsquo;s built for working with them. I tried to extract the patterns behind how he actually uses AI in his data work today. This is the fourth interview in &lsquo;How to use AI with DE&rsquo;.</p>
<p>In this article, we go into four parts: <strong>(1)</strong> How to balance quality with messy data warehouse work, and how to manage agents with Figma for agents. <strong>(2)</strong> We elaborate on the future of the context layer and the return to semantics, <strong>(3)</strong> how Okta for Agents is needed for security, and <strong>(4)</strong> how the future of agentic workloads can be done in teams, whose yap-to-ship ratio is best, and why Amdahl&rsquo;s law still counts.</p>
<h2 id="introducing-the-guest-4-maxime-beauchemin">Introducing the Guest: #4 Maxime Beauchemin</h2>
<p>Our guest in this interview Max Beauchemin, the creator of Airflow and Superset. He&rsquo;s known as one of the OGs of defining how data engineering worked <a href="https://medium.com/free-code-camp/the-rise-of-the-data-engineer-91be18f1e603" target="_blank" rel="noopener noreffer">back in 2017</a>, and founded Preset, the company behind Superset, and currently serves as its CEO.</p>
<p>He is heavily involved in the AI workflow, which is another reason I wanted to interview him for this series, but he has also been building in the space himself: <a href="https://github.com/preset-io/agor" target="_blank" rel="noopener noreffer">Agor</a> (Ag: AI agent + Or: orchestration), earlier tooling like claudette-cli<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and db-agents, an experiment to embed agent context directly inside databases. We&rsquo;ll get into it all.</p>
<p>Max and I talked about many things, among them how to use AI in data engineering, how security plays a role, how shared, secure, context-rich agent workspaces work within teams, and how he uses AI assistants to run his business and ease his life as a CEO.</p>
<p>Max is a true open-source enthusiast, and he wants open source to win. Everything we discuss here is somewhere on GitHub, which I have happily linked throughout the interview.</p>
<h2 id="figma-for-agents-visualizing-tasks-and-jobs-with-agor">Figma for Agents: Visualizing Tasks and Jobs with Agor</h2>
<p>Before we start using Figma for Agents, coordinating them on canvas, we need to ask why we need coordination and orchestration in the first place.</p>
<h3 id="balancing-quality-with-quantity-messy-dwhs">Balancing Quality with Quantity: Messy DWHs</h3>
<p>That&rsquo;s where we started, with the <strong>challenge of messy data warehouse environments</strong> that most people find themselves in. I asked how he balances quality and quantity, aiming for high quality.</p>
<p>Max says that the new models, Opus 4.5 or 4.6<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, are not making many errors anymore and are very clever when they get the right <strong>context</strong> as above with all the database schemas of tables and data types, and even querying it with MCP. He says they almost run in self-serve mode, but he still prefers that users know what they are doing and can either read the generated code or verify the generated numbers on a dashboard or chat results.</p>
<p>But the setup is critical. With <strong>these three prerequisites</strong>, the agents handle almost all queries really well:</p>
<ol>
<li>You need some preparation claude.md/ <code>agents.md</code></li>
<li>Access to SQL (e.g., execute dbt)</li>
<li>Access to MCP or CLI for BI tools (e.g., Superset supports <a href="https://preset.io/blog/meet-sup-supersets-new-cli-for-automation-and-agents/" target="_blank" rel="noopener noreffer">sup!</a>, a CLI to interact with Superset)</li>
</ol>
<p>The only problem, and always has been, is the messy structure and sources that most organizations have, growing from an initial small project into a certain stage. <strong>There are always obscure tables</strong> or strings, timestamps not aligned, or hidden information that is not encoded in code or written down. Or there&rsquo;s the hidden knowledge, like that a certain table shouldn&rsquo;t be used anymore or has bad data, which is known to the people using it but might not be to agents.</p>
<h3 id="the-canvas-in-which-your-agents-can-run-automate-most-ceo-stuff">The Canvas in Which Your Agents Can Run: Automate Most CEO-stuff</h3>
<p>When he recently saw the power of agentic coding, Max went all in and has been building the Figma for agents ever since. Something he can use to collaborate with agents within his company, instead of everyone running the same prompts locally and needing to sync with each other manually. That&rsquo;s when <a href="https://agor.live/" target="_blank" rel="noopener noreffer">Agor</a> was born.</p>
<p>Agor stands for <code>Ag: agent</code> and <code>Or: for orchestration</code>. As the creator of Airflow and CEO of a data company, he knows exactly how a tool needs to improve his workflow. He also called it:</p>
<blockquote>
<p>The goal is to <strong>automate most of the automatable CEO-stuff</strong></p>
</blockquote>













  
<figure><span class="img-expandable" data-full="/blog/figma-for-agents-max-beauchemin/canva.webp">
<a target="_blank" href="/blog/figma-for-agents-max-beauchemin/canva.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">The board: branches as cards, zones as regions, agent sessions, and teammates present live. See full demo <a href="https://www.youtube.com/watch?v=3in0qh7ZH0g" target="_blank" rel="noopener noreffer">Agor Agent Orchestration Demo</a>.</figcaption>
</figure>
<h3 id="building-an-internal-knowledge-base-shared-canvas">Building an Internal Knowledge Base: Shared Canvas</h3>
<p>Agor was Max&rsquo;s answer to &ldquo;how he uses AI beyond a research tool&rdquo;, but doing data modeling, writing data pipelines, even legal or HR roles he added later to Agor, so you can give company-wide roles to agents that can be fed with dedicated documents and context, and triggered by any employee internally. In contrast, others see the jobs and avoid asking the same questions, reusing the output for new queries—Andrej Karpathy&rsquo;s concept of an <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f" target="_blank" rel="noopener noreffer">LLM-maintained shared team wiki</a>—which builds an internal knowledge base.</p>
<p>Initially, when we first chatted, Agor had already changed how he worked as a CEO, but since then, Agor has gone even further. Agor can replace high-level tasks while still being very hands-on by working closely with the code via git worktrees (<a href="https://agor.live/guide/branches" target="_blank" rel="noopener noreffer">branch cards</a> in Agor&rsquo;s UI) and verifying the code in the PRs it produces. Max also added OpenClaw-like features around memory and identity via dedicated Markdown files, such as <code>AGENTS.md</code>, <code>SOUL.md</code>, <code>MEMORY.md</code>, so that Agor&rsquo;s agents can learn from recent runs and carry a purpose and clear instructions. This led to <strong>role-based agents called <a href="https://agor.live/guide/assistants" target="_blank" rel="noopener noreffer">Assistants</a></strong> (use <a href="https://github.com/preset-io/agor-assistant" target="_blank" rel="noopener noreffer">agor-assistant</a> as a template to build your own).</p>













  
<figure><span class="img-expandable" data-full="/blog/figma-for-agents-max-beauchemin/agor-roles.webp">
<a target="_blank" href="/blog/figma-for-agents-max-beauchemin/agor-roles.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Example of different Agor assistants: Saul for legal, or OpEx for observability and so forth. | From the Webinar <a href="https://youtu.be/H7DwINlpE-Y?si=NjOfj1YdoSMPXpUW&amp;t=2556" target="_blank" rel="noopener noreffer">Anatomy of Our Internal Data Agent</a></figcaption>
</figure>
<p>Inspired by OpenClaw&rsquo;s agent loop, Assistants became first-class citizens, persistent AI companions with memory, identity, skills, and scheduled tasks integrated into Agor&rsquo;s canvas, multiplayer workflows, and reachable directly from Slack, for example. Additionally, Agor adds features beyond OpenClaw, such as better multi-user support, RBAC, one-click, full session inspection, and <a href="https://agor.live/guide/features-overview" target="_blank" rel="noopener noreffer">many more</a>.</p>
<p>Asked about the goal of Agor, Max said:</p>
<blockquote>
<p>The initial premise was to <strong>remove DevOps and set up time for other members of the company</strong>. Instead of people needing to connect all the MCPs or CLIs to add API keys, set permissions, or integrate with Slack, the prompt window with the needed context is there and ready to start.</p>
</blockquote>
<blockquote>
<p>[!note] OpenClaw, what is it? And how does Agor compare?<br>
<a href="https://docs.openclaw.ai" target="_blank" rel="noopener noreffer">OpenClaw</a> (formerly ClawdBot) is an open-source agent framework built around a persistent agent loop: a serialized cycle that turns a message into actions, using file-based identity (<code>SOUL.md</code>) and layered memory (<code>MEMORY.md</code>). Agor&rsquo;s Assistants adopt this pattern and extend it with multiplayer boards, RBAC, and canvas-level orchestration.</p>
</blockquote>
<h4 id="visual-and-spatial-memory">Visual and Spatial Memory</h4>
<p>When you do a lot of agent work, it&rsquo;s really hard to keep up with all of it. That&rsquo;s where Agor&rsquo;s visual and spatial overview really helps and is unique in its approach.</p>
<p>It brings the local and private session to a server, where everybody can see and work together on the same queries, and use the insights from other results, as dashboards are built for. So instead of keeping output locally, others can source the artifacts generated by agents, stored as <em><a href="https://agor.live/guide/artifacts" target="_blank" rel="noopener noreffer">Artifacts</a></em> within Agor, ready to use by anyone, with no integration or deployment needed.</p>













  
<figure><span class="img-expandable" data-full="/blog/figma-for-agents-max-beauchemin/artifacts.webp">
<a target="_blank" href="/blog/figma-for-agents-max-beauchemin/artifacts.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Example Artifacts such as AI Ops Command Center, Tool Log triage, these live directly in Agor based on an Agor <a href="https://agor.live/guide/sessions" target="_blank" rel="noopener noreffer">session</a></figcaption>
</figure>
<p>Or how Agor tracks its own spending across sessions:</p>













  
<figure><span class="img-expandable" data-full="/blog/figma-for-agents-max-beauchemin/agor-spending.webp">
<a target="_blank" href="/blog/figma-for-agents-max-beauchemin/agor-spending.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">See demo at <a href="https://youtu.be/H7DwINlpE-Y?si=YOm4hUPUI_YeHScp&amp;t=2313" target="_blank" rel="noopener noreffer">Live Talk: Anatomy of Our Internal Data Agent at Preset (ft. Agor)</a></figcaption>
</figure>
<blockquote>
<p>[!note] Check the full <a href="https://www.youtube.com/watch?v=H7DwINlpE-Y" target="_blank" rel="noopener noreffer">Webinar</a> about the Anatomy of the Internal Data Agent at Preset.</p>
<p>Such as an assistant needing access to all pipelines&rsquo; metadata as <a href="https://youtu.be/H7DwINlpE-Y?si=PWNU7CF03RE3Gdgv&amp;t=1357" target="_blank" rel="noopener noreffer">illustrated data stack</a>.</p>
<p>Or the data <a href="https://youtu.be/H7DwINlpE-Y?si=vBsY4EpbtxjQiB2o&amp;t=1488" target="_blank" rel="noopener noreffer">needs an analytics agent</a> to support, such as self-serve, the data team, and extras such as memory, skills, documentation, etc:</p>
</blockquote>
<h2 id="context-layer-back-to-semantic-layers">Context Layer: Back to Semantic Layers?</h2>
<p>Max also believes that we are <a href="https://preset.io/blog/semantic-layer-is-back/" target="_blank" rel="noopener noreffer">going back to the Semantic layer, or using it for AI</a> as agents benefit from structured information - helping with the data model and SQL part, to make sure it&rsquo;s correct, especially with the needs of AI agents and the persistent challenge of providing trustworthy self-service analytics.</p>
<p>With the shift of semantics outside of the BI tool, versioned, testable, portable, it&rsquo;s a chance for better integration between business domain experts and data engineers. His thinking has evolved since he wrote the article, and Max told me:</p>
<blockquote>
<p>I see two different semantics: the <strong>semantic layer and the YAML</strong>. There are the <strong>hard constraints</strong> — not every area needs that strictness — and then the <strong>softer ones with Markdown</strong> and Agentic Skills, good for 80-90% but with no guarantees.</p>
</blockquote>
<h3 id="agentsmd-for-databases-markdown-stored-inside-the-database-itself"><code>AGENTS.md</code> For Databases: Markdown Stored Inside the Database Itself</h3>
<p>Based on that idea, Max created an experiment to bring the <code>AGENTS.md</code> convention <strong>into the database</strong>. DB-AGENTS reserves a dedicated schema and table, <code>_agents._agents</code>, that holds agent-oriented documentation at different scopes (global, domain, schema, table, and even column). You write the docs locally as markdown files with YAML frontmatter, and a small CLI (<code>dba</code>) deterministically syncs them into that table — since databases don&rsquo;t let you drop files into them, the table becomes the file. Agents then query it at session start the same way they&rsquo;d read an <code>AGENTS.md</code>, making it a natural companion to <code>INFORMATION_SCHEMA</code>: one holds structure, the other holds meaning. Max calls it a &ldquo;soft semantic layer&rdquo;, which maps directly onto the hard-vs-soft split he described above. Check out the repo at <a href="https://github.com/mistercrunch/db-agents" target="_blank" rel="noopener noreffer">db-agents</a>.</p>
<p>With context being key for agents to understand what we humans know, Agor also added a context layer called <a href="https://agor.live/guide/knowledge" target="_blank" rel="noopener noreffer">knowledge</a>. Agor Knowledge acts as a central place where humans and agents can store, organize, connect, and find the context that makes work compound over time, with Slack <a href="https://www.linkedin.com/feed/update/urn:li:activity:7469766677610569728/" target="_blank" rel="noopener noreffer">quickly becoming</a> the main interface to many of the team&rsquo;s agents.</p>
<h2 id="how-do-we-sandbox-agents-for-safe-workflows-okta-for-agents">How Do We Sandbox Agents for Safe Workflows (Okta for Agents)</h2>
<p>Another big topic is security when agents have so much access to powerful CLIs, sometimes root access to systems or databases containing private keys, or just downloading random skills from the internet that may contain hidden secret messages.</p>
<p>Max <a href="https://www.linkedin.com/posts/maximebeauchemin_i-finally-got-to-around-to-test-driving-clawdbot-activity-7423272818848550912-FSCn" target="_blank" rel="noopener noreffer">coined</a> the idea of <strong>Okta for Agents</strong>, which I found super interesting, and something I believe will become ever more important if we want to find a healthy way of working with agents in enterprises or with sensitive data. Okta for Agents means working around identity, scoped delegated permissions, leases, and audit logs.</p>
<p>When asked how he&rsquo;s managing security, verifying what Agor or the agents are doing, Max responded:</p>
<blockquote>
<p>I let the workers run in <code>god mode</code><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, but using <strong>dedicated environments/sandboxes</strong>, hooked to a dedicated git worktree repo, it can run autonomously and solve problems on an initial prompt, visualized in a <strong>shared canvas</strong> style.</p>
</blockquote>
<p>I asked how he sees Okta for Agents being implemented. We desperately need it, he said, granting agents permissions like impersonation. Delegating the permission is an OAuth. With the roles, we can <strong>scope permissions strongly</strong>. E.g., the sales agent only has access to sales documents.</p>
<p>When asked at what level to integrate the Okta security layer, Max said it hasn&rsquo;t been solved yet. Still, he sees it as the same question: whether we have 50 agents or 50 users who use a platform, both need a security layer.</p>
<p>Likewise, Max shared:</p>
<blockquote>
<p>I trust agents the same way as I would an employee.</p>
</blockquote>
<blockquote>
<p>[!note] What does &ldquo;Okta for Agents&rdquo; mean in more details?<br>
Okta is the identity layer companies put in front of their tools: it authenticates who you are, decides which systems you can open, and logs what you did. Max&rsquo;s point is that agents need the same layer. E.g. Clawdbot, when he tried it, was effectively a DIY IAM manager for agents, config hell and all.</p>
<p>The twist is that it adds a whole new dimension to RBAC. It&rsquo;s not just &ldquo;the bot gets an email account&rdquo; — it&rsquo;s &ldquo;the bot gets an email account, but can only read mine, and via MCP rather than as a real user.&rdquo; Not so different from onboarding a human personal assistant, except this assistant can help with nearly everything, so the blast radius is much bigger.</p>
<p>Max&rsquo;s own example: he saw a 1Password skill and immediately backed off then reconsidered, wondering whether the bot should have its own 1Password account with only safe credentials shared into it. Which is exactly the problem: you can be strict on paper, but the moment the agent has your email, Slack, and calendar, it can leak private things all day. (Full discussion at <a href="https://www.linkedin.com/posts/maximebeauchemin_i-finally-got-to-around-to-test-driving-clawdbot-activity-7423272818848550912-FSCn/" target="_blank" rel="noopener noreffer">this post</a>)</p>
</blockquote>
<blockquote>
<p>[!warning] Security is critical. Here are examples when it&rsquo;s gone bad<br>
<a href="https://alexeyondata.substack.com/p/how-i-dropped-our-production-database" target="_blank" rel="noopener noreffer">How I Dropped Our Production Database and Now Pay 10% More for AWS</a>, or <a href="https://x.com/lifeof_jer/status/2048103471019434248" target="_blank" rel="noopener noreffer">another one</a>, or when <a href="https://www.engadget.com/ai/13-hour-aws-outage-reportedly-caused-by-amazons-own-ai-tools-170930190.html" target="_blank" rel="noopener noreffer">13-hour AWS outage reportedly caused by Amazon&rsquo;s own AI tools</a>. Or also just hacks by getting <a href="https://www.blef.fr/someone-tried-to-hack-us" target="_blank" rel="noopener noreffer">attacked via GitHub PRs</a> or <a href="https://x.com/ZackKorman/status/2018386838101086446" target="_blank" rel="noopener noreffer">How secret instructions injected into skills</a>.</p>
</blockquote>
<h3 id="declarative-and-non-deterministic-outcomes">Declarative and Non-deterministic Outcomes?</h3>
<p>Related to security is the deterministic, repeatable behavior of data sets with the same input. Agents are the opposite: probabilistic. I was curious to hear from Max, who initially <a href="https://maximebeauchemin.medium.com/functional-data-engineering-a-modern-paradigm-for-batch-data-processing-2327ec32c42a" target="_blank" rel="noopener noreffer">defined the functional data engineering paradigm</a> for deterministic and <a href="https://en.wikipedia.org/wiki/Idempotence" target="_blank" rel="noopener noreffer">idempotent</a> batch data processing, what he thinks about the non-deterministic outcomes of agents, specifically with large language models.</p>
<p>Max said, regarding declarative definitions, that he finds a <code>claude.md</code> is usually sufficient for most tasks that have a git repo, more context, and an issue or PR to work with, given the initial prompts come from users who know what they are doing.</p>
<p>Regarding <strong>reliability</strong>, Max thinks about using <strong>good methodology</strong> references. Agents get it and understand it. E.g., data modeling practices such as Kimball are still valid, or the approach shared by him with <a href="https://preset.io/blog/introducing-entity-centric-data-modeling-for-analytics/" target="_blank" rel="noopener noreffer">Entity-Centric Data Modeling (ECM)</a>, he says, and when prompted to model in those patterns, agents follow them well (either via research or provided).</p>
<p>The other part is that some non-dangerous work can have <strong>vibe data pipelines</strong>, and there&rsquo;s no danger. And there are cognitive-depth tasks, such as a complex Spark cluster, where you can&rsquo;t just debug quickly with large data sets.</p>
<p>Also, the field varies: <strong>not every area is getting agentic-piled as fast</strong>. E.g., platform demands go through the roof (see GitHub outages), so we have 10-20x the platform needs, but at the same time, the work is critical to be correct. So it depends.</p>
<h2 id="future-of-agentic-workload-and-canvas-development-in-teams">Future of Agentic Workload, and Canvas Development in Teams</h2>
<p>When asked about how Agor has <strong>changed how they at Preset develop products</strong> (if at all?), or made them more effective, Max said:</p>
<blockquote>
<p>There are more agents than humans nowadays. Everyone has a Claude Max plan, and agents handle almost all code writing.</p>
</blockquote>
<p>And on a personal level:</p>
<blockquote>
<p>I haven&rsquo;t written a function by hand for a long time, and I might not anymore — except when I feel nostalgic.</p>
</blockquote>
<p>He also thinks that the <a href="https://www.linkedin.com/feed/update/urn:li:activity:7462251894195974144/" target="_blank" rel="noopener noreffer">Yap-to-Ship Ratio</a>, a metric that he <a href="https://www.linkedin.com/feed/update/urn:li:activity:7462251894195974144/" target="_blank" rel="noopener noreffer">announced half-jokingly on LinkedIn</a>, describing people&rsquo;s velocity by just getting stuff done without involving others at every step, will be very <strong>low-yap for 10x engineers</strong>, as they solve the problem and ship a solution without much back and forth.</p>
<p>They deploy it somewhere for others to use, not only for human consumption, but as a solution or CLI that other agents can use to discover further and solve their problems. A high Yap-to-Ship ratio would mean <strong>lots of human interaction,</strong> which is the clear <strong>new bottleneck</strong>.</p>
<p>As human <strong>code review gets bottlenecked</strong>, I asked how he does the review. He said he uses Codex with sub-agents to review, ensuring everything is DRY (Don&rsquo;t Repeat Yourself) and that all expected callbacks are made.</p>
<p>You can also ask the operator assistant agents if you are not sure whether an implementation is correct.</p>
<h3 id="amdahls-law-cant-go-faster-if-not-end-to-end">Amdahl&rsquo;s Law: Can&rsquo;t Go Faster if not End-to-end</h3>
<p>One <a href="https://youtu.be/g8BuAtM3fp4?si=Dl_utL0jJfkOdSlx&amp;t=1866" target="_blank" rel="noopener noreffer">bottleneck is still Amdahl&rsquo;s Law</a>. We can speed up tooling by using extremely fast agents, but unless the end-to-end workload is sped up, we only increase by a 2-3x factor, not 10 or 100 as any one tool does. This also overlaps with Max&rsquo;s Yap-to-Ship ratio: if PRs need the <em>human in the loop</em> to review many of them, the overall speed at which we build is not faster.</p>
<p>Another side effect is that it takes a lot of context switching. Max said he has ten active sessions in Agor. He is good at context switching (maybe also learned through recent Agor workflow? 🙂).</p>
<h3 id="predictions-for-2026">Predictions for 2026</h3>
<p>It&rsquo;s hard to predict the future with AI, but Max took a stab and shared his predictions for 2026 and categorized them into wired and tired:</p>













  
<figure><span class="img-expandable" data-full="/blog/figma-for-agents-max-beauchemin/predictions.webp">
<a target="_blank" href="/blog/figma-for-agents-max-beauchemin/predictions.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Find the full talk at <a href="https://youtu.be/H7DwINlpE-Y?si=gk7rO4v3ev6p0Evn&amp;t=446" target="_blank" rel="noopener noreffer">Webinar</a>.</figcaption>
</figure>
<h2 id="maxs-ai-setup-for-data-engineering-work-and-managing-his-company">Max&rsquo;s AI Setup for Data Engineering Work and Managing His Company</h2>
<p>We end this interview with Max&rsquo;s setup for working with agents, since we didn&rsquo;t have time to go into full details on the call. I&rsquo;m sharing the one he shared <a href="https://www.linkedin.com/posts/maximebeauchemin_my-current-ai-setup-for-software-engineering-activity-7426190969345228801-fyZL" target="_blank" rel="noopener noreffer">four months ago</a>. I&rsquo;m sure it changes almost daily. Still, it helps us get a good overview of his software engineering stack for the team at Preset, as well as his personal local computer stack.</p>
<p>For <strong>software engineering and data engineering</strong>:</p>
<ul>
<li>Preset <em>team</em> instance of Agor behind VPN with a dozen boards, boards are mostly repo-oriented. Full Unix impersonation, backed by PostgreSQL.</li>
<li>doing most of my work on board with the <code>agor-openclaw</code> framework, agent is pushing projects across a kanban-type layout: tons of new automation there. Agent checks on agents, prompts them, moves worktrees to <strong>&ldquo;needs human review&rdquo;</strong> zone if/when needed</li>
<li>coding workflow is Opus 4.6 as a planner, Sonnet 4.5 / Opus 4.6 for most coding, Codex 5.3 as the reviewer (god it&rsquo;s so good)</li>
<li>data engineering stuff: dbt/airflow repo + Superset MCP, superset-sup</li>
<li>&ldquo;Command center&rdquo; is agor-openclaw on Opus 4.6, monitors other <strong>agents, intricate <code>HEARTBEAT.md</code> with pseudocode</strong> to make a decision for coding project (worktree) on that board</li>
</ul>
<p>And his new <strong>&ldquo;Personal assistant&rdquo; local instance</strong> of Agor (brand new/sensitive):</p>
<ul>
<li>Beefy Mac Studio at home</li>
<li>connected to &ldquo;productivity&rdquo; tools (google-workspace-mcp)</li>
<li>connected to Slack through a semi-homegrown skill &mdash; read-only for now, mostly to summarize activity</li>
<li>connected to Notion MCP</li>
<li>connected to &ldquo;contracts&rdquo; repo, where I sync with Google Drive for all Preset contracts</li>
<li>connecting to Hubspot soon</li>
<li>goal is to <strong>automate most of the automatable CEO-stuff</strong> as discussed above</li>
</ul>
<h2 id="coming-up">Coming up</h2>
<p>We&rsquo;ve learned how to use Figma for agents with Agor and to collaboratively work as a team, using shared prompts and creating artifacts. We&rsquo;ve seen how Okta for Agents is needed but really hard to implement, and how the future of AI is mostly about context and how to integrate it well. Plus, we learned how Max automates many tasks as a CEO with dedicated AI assistants and still produces low-level code with the same assistants for both his personal and company-wide needs.</p>
<p>I hope you enjoyed this fourth interview with Max. Huge thanks to Max for taking the time to speak with me (twice!) and for sharing his experience with all of us. Follow him on <a href="https://www.linkedin.com/in/maximebeauchemin/" target="_blank" rel="noopener noreffer">LinkedIn</a>, <a href="https://github.com/mistercrunch" target="_blank" rel="noopener noreffer">GitHub</a>, or on <a href="https://preset.io/blog/" target="_blank" rel="noopener noreffer">Preset Blog</a>, where he shares his distilled thoughts on the ecosystem, and obviously, if you want to know more about Agor, check it out at <a href="https://github.com/preset-io/agor" target="_blank" rel="noopener noreffer">Agor GitHub repo</a>.</p>
<p>Max shares a lot of his ideas and thoughts online. Here are some further articles and interviews to read/watch:</p>
<ul>
<li><a href="https://preset.io/blog/ai-enablement-engineer/" target="_blank" rel="noopener noreffer">AI Enablement Engineer: The Highest-Leverage Role in Tech</a></li>
<li>Check the Joe Reis interview where Max shares more on the discussion <a href="https://www.youtube.com/watch?v=DLYnHGoI9z8" target="_blank" rel="noopener noreffer">From Airflow to AI Agents: On Building Agor and Running a Company with AI Agents</a></li>
</ul>
<p>More interviews are coming out, so please share feedback, questions you might want to ask, or your experience working with AI in the data space. We&rsquo;re all in this together, figuring it all out.</p>
<hr>
<pre class=""><em>Full article published at <a href="https://motherduck.com/blog/figma-for-agents-airflow-creator-maxime-beauchemin/" target="_blank" rel="noopener noreferrer">MotherDuck.com</a> - written as part of <a href="/services/">my services</a></em></pre>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://github.com/mistercrunch/claudette-cli" target="_blank" rel="noopener noreffer">claudette-cli</a>, a CLI for managing git worktrees, originally built for Apache Superset development.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>when we first discussed in February 2026&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>god mode means scoped/sandboxed/audited environments, not uncontrolled access&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>Book Recommendations and Notes</title>
    <link>https://www.ssp.sh/books/</link>
    <pubDate>Thu, 30 Jul 2026 10:30:00 &#43;0100</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/books/</guid><enclosure url="https://www.ssp.sh/books/featured-image.webp" type="image/webp" length="0" /><description><![CDATA[<p>These are my recently read books and some comments and notes from when I read them.</p>
<p>As I love books and recommendations by others, I want to share them in a collective format so others can read gems of books, as I think books are still the best way to read new information these days. Even more with the fast pace we are going at, as books are well structured and made for the long term.</p>
<h2 id="book-recommendations-ive-read">Book Recommendations I&rsquo;ve Read</h2>
<p>Over the years, I have made recommendations in <a href="/writers-room/" rel="">Writer&rsquo;s Room</a>, <a href="https://subscribe.ssp.sh" target="_blank" rel="noopener noreffer">my newsletter</a>, on my <a href="https://now.ssp.sh" target="_blank" rel="noopener noreffer">now page</a> or in person. Here I have collected and curated them over the years including some comments. I hope you enjoy.</p>
<h3 id="2026">2026</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/en/book/show/230337105-poems-prayers" target="_blank" rel="noopener noreffer">Poems &amp; Prayers</a></strong> (Matthew McConaughey): Another one read by Matthew himself. It&rsquo;s about life and its rhymes. He said life is like rhymes as he likes them, sounds good, and prayer because of the meaning. Together applied to life, it&rsquo;s like a dace to the day. He also says he has a rhythm in his head when he wakes up, and then he dances to it somehow during the day, and life fits into the song, making his day like a dance. Also, people say he is so laid back, but it&rsquo;s because he has a plan, he does not rush, he takes time. So that&rsquo;s on purpose. Something I also actively try to do in my life. Not yet finished.</li>
<li><strong><a href="https://www.goodreads.com/book/show/215581569-maintenance-of-everything" target="_blank" rel="noopener noreffer">Maintenance of Everything</a></strong> (Stewart Brand): This is just so good, it tells the story of how maintenance is everything if you rely on a boat on the sea, for example. Making the product easy to repair so you can fix it even in a rough sea with limited tools. It&rsquo;s an analogy you can apply to every part of life, and the way the story is told is so great. I couldn&rsquo;t stop listening to the audiobook. I really recommend it to everyone; I&rsquo;m sure it will give you lots of insights into your current work.</li>
<li><strong><a href="https://www.goodreads.com/en/book/show/241381433-big-time" target="_blank" rel="noopener noreffer">Big Time</a></strong> (Laura Vanderkam):  Started with the goal of getting more out of my time. So far, I&rsquo;ve learned that time management is essentially about controlling time, but it&rsquo;s not the only way. And that &ldquo;Shifting Left&rdquo; is not only used in <a href="https://www.ssp.sh/blog/shifting-left/" target="_blank" rel="noopener noreffer">data engineering</a>, Laura describes <strong>doing tasks long before they are due</strong> as shifting left too. Good reminder of how to use time better, nothing revolutionary, but lots of real-life examples from her and her clients.</li>
<li><strong><a href="https://www.goodreads.com/book/show/12530.The_Writing_Life" target="_blank" rel="noopener noreffer">The Writing Life</a></strong> (Annie Dillard): A poetic way of what it means to be a writer. Great storytelling. Learning how to write with a family as part of her stories.</li>
</ul>
<h3 id="2025">2025</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/60387307-novelist-as-a-vocation" target="_blank" rel="noopener noreffer">Novelist as a Vocation</a></strong> (Haruki Murakami): Such an amazing book on writing. It really reminded me of my writing style, as he translates words and tries to write beautifully for the sake of beauty, not to impress the reader. No timeline. It takes as long as it takes until the quality is good. And he likes a good challenge to keep him engaged, e.g., when he goes abroad to start from scratch. His style is so unique and amazing. It&rsquo;s a joy to listen to.</li>
<li><strong><a href="https://www.goodreads.com/book/show/52838315-greenlights" target="_blank" rel="noopener noreffer">Greenlights</a></strong> (Matthew McConaughey): This book was so real. Matthew reads and shares from his 35 years of journaling and life. So inspiring and [[Writing from The Heart|straight from the heart]]. It was interesting and motivating to hear from a Hollywood actor and celebrity what truly matters: family and <strong>keep livin</strong>. Alright, aaaalright, alriiight. → I listened to the audiobook, and his Texan accent was the best. He was rhyming, slowing down, calming down, getting loud, all of it. It was like sitting next to him and listening to your dad or uncle.</li>
<li><strong><a href="https://www.goodreads.com/book/show/52500766-consider-this" target="_blank" rel="noopener noreffer">Consider This</a></strong> (Chuck Palahniuk): The author of Fight Club shares how to write if you were his student. A masterclass if you want to enhance your writing.</li>
<li><strong><a href="https://www.goodreads.com/book/show/135528744-you-can-negotiate-anything-by-herb-cohen" target="_blank" rel="noopener noreffer">You Can Negotiate Anything</a></strong> (Herb Cohen): Interesting perspective that you can negotiate even at places you&rsquo;d never thought of, even at a shopping mall. Always keep in mind what the other&rsquo;s objective is. There are always three elements that are part of a negotiation: information, time, and power.</li>
<li><strong><a href="https://www.goodreads.com/book/show/44573476-the-holy-bible" target="_blank" rel="noopener noreffer">The Bible</a></strong>: I also started to read the Bible, the most sold book on earth. I wanted to understand what&rsquo;s in there. Obviously, it&rsquo;s so massive, and it&rsquo;s actually not one book, but a collection of many. I get a similar sensation to one of my favorite books <strong><a href="https://www.goodreads.com/book/show/29093292-the-daily-stoic?ac=1" target="_blank" rel="noopener noreffer">The Daily Stoic (366 Meditations)</a></strong>. It&rsquo;s like a daily meditation. While Ryan Holiday&rsquo;s book helps me feel calmer, the Bible nourishes my love and care for others.</li>
<li><strong><a href="https://www.goodreads.com/book/show/57933306-stolen-focus" target="_blank" rel="noopener noreffer">Stolen Focus: Why You Can&rsquo;t Pay Attention</a></strong> (Johann Hari): This book inspired me to write an article about flow myself: <strong><a href="/blog/finding-flow/" rel="">Finding Flow</a></strong>. Super insightful and probably will make you want to ban social media by the government.</li>
<li><strong><a href="https://www.goodreads.com/book/show/209933737-useful-not-true" target="_blank" rel="noopener noreffer">Useful Not True</a></strong> (Derek Sivers): Derek is the master of stripping away. This book has so much wisdom in short one-page stories from Derek&rsquo;s life. It&rsquo;s a gem. Perfect to re-read, over and over. It&rsquo;s next to my sleeping place to just grab.</li>
<li><strong><a href="https://www.goodreads.com/book/show/67896.Tao_Te_Ching" target="_blank" rel="noopener noreffer">Tao Te Ching</a></strong> (Lao Tzu): Dao and Daoism: Men - earth - universe - dao. If you don&rsquo;t want to read the bible, this is a none religious way of getting solitude and compassion.</li>
</ul>
<h3 id="2024">2024</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/213618261-good-work" target="_blank" rel="noopener noreffer">The Good Work</a></strong> (Paul Millerd): A continuation of the famous Pathless Path, not as good, but OK. Please read it if you haven&rsquo;t yet.</li>
<li><strong><a href="https://www.goodreads.com/book/show/42291479-the-extended-mind" target="_blank" rel="noopener noreffer">The Extended Mind</a></strong> (Annie Murphy Paul): A powerful book. I started reading it again.</li>
<li><strong><a href="https://www.goodreads.com/book/show/10645233-do-the-work" target="_blank" rel="noopener noreffer">Do The Work</a></strong> (Steven Pressfield): It&rsquo;s not the best book, maybe also because I&rsquo;ve heard or read lots of the content already elsewhere, but I appreciated that it was short and that he didn&rsquo;t try to extend it to 300 pages.</li>
</ul>
<h3 id="2023">2023</h3>
<ul>
<li><strong><a href="https://www.amazon.com/Slow-Productivity-Accomplishment-Without-Burnout/dp/0593544854" target="_blank" rel="noopener noreffer">Slow Productivity</a></strong> (Cal Newport): This is a fantastic book. It showcases why we should quit the race of everyday life and slow down for a compounding effect instead of overnight success. The same is true for money: investing long term instead of gambling in a casino. He describes that instead of pseudo productivity, which is the norm these days, where we try to be as busy as possible to showcase we are doing something, we should try to be as productive as possible.</li>
<li><strong><a href="https://www.goodreads.com/book/show/122765395-elon-musk" target="_blank" rel="noopener noreffer">Elon Musk</a></strong> (Walter Isaacson): It&rsquo;s very long, but I loved every page (or word as I listened to it on Audible). It is inspiring. It shows how an exceptional, hard-working Musk is doing everything for humanity in an unhealthy way. He is also, to an extent, sick. He has his dark sides that just come out sometimes, which are also a sign of his mental state that he got from his dad, and also from his hard work and pressure. He is thriving in chaos. Whenever there is a calm time, he will do something new as he can&rsquo;t stand the status quo.</li>
<li><strong><a href="https://www.goodreads.com/book/show/13425570-how-will-you-measure-your-life" target="_blank" rel="noopener noreffer">How Will You Measure Your Life?</a></strong> (Clayton M. Christensen): A good book if you want to know more than just work and how you&rsquo;ll measure it. This book was reassuring and strengthened many things I already knew. It reminded me that finding your principles is the key. Follow them and align your life so you have a happy life. It was also helpful for me as a dad and family member to pass on the same principles and values to my kids. Be intentional about your values. It&rsquo;s hard to find them. They won&rsquo;t be sent to you. You need to make them. But be aware that it is a process, not an event.</li>
<li><strong><a href="https://www.goodreads.com/en/book/show/62192519" target="_blank" rel="noopener noreffer">The Good Enough Job</a></strong> (Simone Stolzoff): A compelling book that challenges our conventional thinking about work. Instead of idolizing our jobs or incessantly chasing a better one, Stolzoff advocates for finding satisfaction in a &ldquo;good enough&rdquo; job. His ideas offer a refreshing contrast to the pervasive Instagram-era narrative that equates career success with personal fulfillment. A highly recommended read for anyone feeling pressured by the modern-day cult of work.</li>
<li><strong><a href="https://www.goodreads.com/en/book/show/61992094" target="_blank" rel="noopener noreffer">The Daily Dad</a></strong> (Ryan Holiday): This book is a sequel to one of my all-time favorite reads, The Daily Stoic. In the same tradition of offering daily philosophical advice, this book focuses on the challenges and rewards of parenthood. TODO</li>
<li><strong><a href="https://www.goodreads.com/book/show/42291479-the-extended-mind" target="_blank" rel="noopener noreffer">The Extended Mind</a></strong> (Annie Murphy Paul): An exploration of the intriguing ways our environment influences our thinking processes. Discover the surprising ways in which experts think beyond their brains, how harder thinking often leads to fewer results and the controversy over brain-training games and smart pills. Explore how we can use tools beyond the brain, such as the Body Scan technique and meditation, to tap into our intuition and sensations, and learn about the significance of the amygdala in our responses to stress.</li>
<li><strong><a href="https://www.goodreads.com/book/show/43062156-all-the-wrong-moves" target="_blank" rel="noopener noreffer">All the Wrong Moves</a></strong> (Sasha Chapin): A captivating narrative where the world of chess serves as a backdrop for introspection and self-discovery. A story that shows how the love of chess can fully dominate one&rsquo;s life, as it&rsquo;s the most beautiful and worst thing in life at the same time.</li>
</ul>
<h3 id="2022">2022</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/40121378-atomic-habits" target="_blank" rel="noopener noreffer">Atomic Habits</a></strong> (James Clear): The main argument: If you want to add a new habit, chain it to an existing one to make it stick.</li>
<li><strong><a href="https://www.goodreads.com/book/show/59616977-building-a-second-brain" target="_blank" rel="noopener noreffer">Building a Second Brain</a></strong> (Tiago Forte): I knew most of it from his articles, podcasts, etc., but now it&rsquo;s also available as a book.</li>
<li><strong><a href="https://www.amazon.com/Getting-Things-Done-Stress-Free-Productivity-ebook/dp/B00KWG9M2E" target="_blank" rel="noopener noreffer">Getting Things Done</a></strong> (David Allen): This book is life-changing if you apply its principles correctly to your life.</li>
<li><strong><a href="https://www.goodreads.com/book/show/34507927-how-to-take-smart-notes" target="_blank" rel="noopener noreffer">How to Take Smart Notes</a></strong> (Sönke Ahrens): The base for a Second Brain and where Sönke reveals how our brain is wired and how we can implement a note-taking style that supports our brain, mainly with the method called Zettelkasten.</li>
<li><strong><a href="https://www.goodreads.com/book/show/4099.The_Pragmatic_Programmer" target="_blank" rel="noopener noreffer">The Pragmatic Programmer</a></strong> (David Thomas &amp; Andrew Hunt): Although a lot was clear, summarizing it and putting it together as one piece, plus hearing it from two professionals, was very helpful and suitable for applying to my work.</li>
<li><strong><a href="https://www.goodreads.com/book/show/10569.On_Writing" target="_blank" rel="noopener noreffer">On Writing</a></strong> (Stephen King): He gives deep insights into the life of a successful writer.</li>
</ul>
<h3 id="2021">2021</h3>
<ul>
<li><strong><a href="https://www.amazon.com/Love-Languages-Secret-that-Lasts/dp/080241270X" target="_blank" rel="noopener noreffer">The 5 Love Languages</a></strong> (Gary Chapman): Really eye opening, as sometimes we want to show love to our spouse, but becausewe give attention, or buy a gift, but if it doesn&rsquo;t match the &ldquo;love language&rdquo; of the reciever, it won&rsquo;t arrive properly. So knowing each others love language, and communicate in that, is huge for any relationship.</li>
</ul>
<h3 id="2020">2020</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/43306206-the-courage-to-be-disliked" target="_blank" rel="noopener noreffer">The Courage to Be Disliked</a></strong> (Ichiro Kishimi, Fumitake Koga): Great book to learn new psychology, especially the Alfred Adler theories of individual psychology and Adlerian therapy. And learn about Interpersonal Relationship. This books makes you stronger.</li>
<li><strong><a href="https://sivers.org/m" target="_blank" rel="noopener noreffer">Your Music and People</a></strong> (Derek Sivers): Fantastic. It tells the story about how to sell your music, which is an analogy to every business.</li>
<li><strong><a href="https://www.goodreads.com/book/show/18007504-mindwise" target="_blank" rel="noopener noreffer">Mindwise: How We Understand What Others Think, Believe, Feel, and Want</a></strong> (Nicholas Epley): Approaching our mind from an angle you probably haven&rsquo;t thought of. Very insightful.</li>
</ul>
<h3 id="2019">2019</h3>
<ul>
<li><strong><a href="https://www.amazon.com/Thirst-Story-Redemption-Compassion-Mission/dp/B07DP4HN7S" target="_blank" rel="noopener noreffer">Thirst: A Story of Redemption, Compassion, and a Mission to Bring Clean Water to the World</a></strong> (Scott Harrison): This book is truly heart-touching and lets you rethink today&rsquo;s world and donations. Very well-written book.</li>
<li><strong><a href="https://www.amazon.com/Doesnt-Have-Be-Crazy-Work/dp/B07G8L5NZ9" target="_blank" rel="noopener noreffer">It Doesn&rsquo;t Have to Be Crazy at Work</a></strong> (Jason Fried &amp; David Heinemeier Hansson): Talks about the crazy work hours usually suggested or heard at places like Google, Amazon, etc. to be able to achieve something. They tell you how you can tackle it otherwise.</li>
</ul>
<h3 id="2018">2018</h3>
<ul>
<li><strong><a href="https://amzn.to/2zpGu8A" target="_blank" rel="noopener noreffer">Principles: Life and Work</a></strong> (Ray Dalio): I recommend this to everyone out there. The first chapters where he talks about himself you can skip, but the rest is worth reading or listening to. TODO</li>
<li><strong><a href="https://www.audible.com/pd/Self-Development/Unshakeable-Audiobook/B01MUDREFG" target="_blank" rel="noopener noreffer">Unshakeable: Your Financial Freedom Playbook</a></strong> (Tony Robbins): If you want to save some money in the long run, please read it. It is outstanding and straight to the point!</li>
<li><strong><a href="https://www.audible.com/pd/Nonfiction/The-Tao-of-Seneca-Audiobook/B01AIXEJKU" target="_blank" rel="noopener noreffer">The Tao of Seneca: Practical Letters from a Stoic Master</a></strong> (Seneca): Puts me back to the essentials of life whenever I feel all over the place.</li>
<li><strong><a href="https://www.amazon.com/Tribe-Mentors-Short-Advice-World/dp/1328994961" target="_blank" rel="noopener noreffer">Tribe of Mentors: Short Life Advice from the Best in the World</a></strong> (Tim Ferriss): Entirely recommendable.</li>
</ul>
<div class="details admonition info open">
        <div class="details-summary admonition-title "><i class="icon admonition-icon icon-info"></i>Not Complete<i class="details-icon  admonition-icon admonition-icon-arrow-right"></i></div>
        <div class="details-content">
            <div class="admonition-content">These are not all books I&rsquo;ve read, but some of the recommendations and that I shared during the years. I will constantly add up more, and potentially link it to my more detailed note of each book.</div>
        </div>
    </div>
<h2 id="general-recommended-books">General Recommended Books</h2>
<p>These are some of my all-time favorite books that I recommend to everyone.</p>
<h3 id="top-books">Top Books</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/29093292-the-daily-stoic" target="_blank" rel="noopener noreffer">The Daily Stoic (366 Meditations)</a></strong> (Ryan Holiday): One of my all-time favorite books. Daily meditations that help me feel calmer and more grounded.</li>
<li><strong><a href="https://www.goodreads.com/book/show/52523856-hell-yeah-or-no" target="_blank" rel="noopener noreffer">Hell Yeah or No</a></strong> (Derek Sivers): Anything by Derek Sivers is worth reading. His writing is stripped to the essentials and packed with wisdom.</li>
<li><strong><a href="https://www.goodreads.com/book/show/58188742-how-to-live" target="_blank" rel="noopener noreffer">How to Live</a></strong> (Derek Sivers): The second book by Derek on top list. He writes 27 conflicting, and short chapters on how to live life. He has done so much, that he writes almost each from experience. As it&rsquo;s short, each chapter can give you a nudge and insight into your own life, and what we should strive, and more importantly, what not. Be aware, every unnecessary word is removed and stripped to the bare essentials. This means you can&rsquo;t read more than two chapters in one go as it&rsquo;s that condensed and makes you think a lot.</li>
</ul>
<h3 id="business">Business</h3>
<ul>
<li><strong><a href="https://www.amazon.com/Doesnt-Have-Be-Crazy-Work/dp/B07G8L5NZ9" target="_blank" rel="noopener noreffer">It Doesn&rsquo;t Have to Be Crazy at Work</a></strong> (Jason Fried &amp; David Heinemeier Hansson): Challenges the norm of crazy work hours and shows you can achieve more with less chaos.</li>
<li><strong><a href="https://www.goodreads.com/book/show/25744928-deep-work" target="_blank" rel="noopener noreffer">Deep Work</a></strong> (Cal Newport): Essential reading on focused work in a distracted world.</li>
</ul>
<h3 id="self-help--life-philosophy">Self-Help &amp; Life Philosophy</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/11468377-thinking-fast-and-slow" target="_blank" rel="noopener noreffer">Thinking, Fast and Slow</a></strong> (Daniel Kahneman): A deep dive into how our minds work, exploring System 1 and System 2 thinking.</li>
<li><strong><a href="https://www.goodreads.com/book/show/60088355-the-pathless-path" target="_blank" rel="noopener noreffer">The Pathless Path</a></strong> (Paul Millerd): Challenges the &ldquo;default path&rdquo; (going to school, work, mary, and have a family) and following your instict. Unconventional way. The new 4 hour work week book. One that I recommend the most lately. I wrote about <a href="https://pathless.ssp.sh" target="_blank" rel="noopener noreffer">Finding my Pathless Path</a>.</li>
<li><strong><a href="https://www.goodreads.com/book/show/54785515-four-thousand-weeks" target="_blank" rel="noopener noreffer">Four Thousand Weeks: Time Management for Mortals</a></strong> (Oliver Burkeman): A refreshing take on time management that embraces our mortality rather than fighting it.</li>
<li><strong><a href="https://www.goodreads.com/book/show/34536488-principles" target="_blank" rel="noopener noreffer">Principles</a></strong> (Ray Dalio): Life and work principles from one of the world&rsquo;s most successful investors.</li>
<li><strong><a href="https://www.goodreads.com/book/show/26329.Emotional_Intelligence" target="_blank" rel="noopener noreffer">Emotional Intelligence</a></strong> (Daniel Goleman): Understanding and managing emotions for better relationships and decisions.</li>
</ul>
<h3 id="-5-star-picks-goodreads">⭐ 5-Star Picks (GoodReads)</h3>
<ul>
<li><strong><a href="https://www.goodreads.com/book/show/53343.On_Writing_Well" target="_blank" rel="noopener noreffer">On Writing Well</a></strong> (William Zinsser): The classic guide to writing nonfiction. Essential for clear writing.</li>
<li><strong><a href="https://www.goodreads.com/book/show/13525945-so-good-they-can-t-ignore-you" target="_blank" rel="noopener noreffer">So Good They Can&rsquo;t Ignore You</a></strong> (Cal Newport): Why skills trump passion in the quest for work you love.</li>
<li><strong><a href="https://www.goodreads.com/book/show/13099738-steal-like-an-artist" target="_blank" rel="noopener noreffer">Steal Like an Artist</a></strong>, <strong><a href="https://www.goodreads.com/book/show/18290401-show-your-work" target="_blank" rel="noopener noreffer">Show Your Work!</a></strong>, &amp; <strong><a href="https://www.goodreads.com/book/show/40591677-keep-going" target="_blank" rel="noopener noreffer">Keep Going</a></strong> (Austin Kleon): Probably <em>the</em> best book on creativity and if you are a creative yourself. It&rsquo;s really helped me sharing more of the process and how creative works. It&rsquo;s a trilogy, I listened the audiobook, which you get three for one, fully recommend it.</li>
<li><strong><a href="https://www.goodreads.com/book/show/17465707-still-writing" target="_blank" rel="noopener noreffer">Still Writing</a></strong> (Dani Shapiro): Honest reflections on the creative life.</li>
<li><strong><a href="https://www.goodreads.com/book/show/41881472-the-psychology-of-money" target="_blank" rel="noopener noreffer">The Psychology of Money</a></strong> (Morgan Housel): Best book on understanding money and behavior.</li>
<li><strong><a href="https://www.goodreads.com/book/show/6346975-moonwalking-with-einstein" target="_blank" rel="noopener noreffer">Moonwalking with Einstein</a></strong> (Joshua Foer): Fascinating journey into memory and how it works.</li>
<li><strong><a href="https://www.goodreads.com/book/show/6480781-open" target="_blank" rel="noopener noreffer">Open</a></strong> (Andre Agassi): Raw, honest autobiography. Surprisingly insightful about life and finding yourself.</li>
<li><strong><a href="https://www.goodreads.com/book/show/11878168-anything-you-want" target="_blank" rel="noopener noreffer">Anything You Want</a></strong> (Derek Sivers): 40 lessons for entrepreneurs. Short, punchy, full of wisdom.</li>
<li><strong><a href="https://www.goodreads.com/book/show/28257707-the-subtle-art-of-not-giving-a-f-ck" target="_blank" rel="noopener noreffer">The Subtle Art of Not Giving a F*ck</a></strong> (Mark Manson): Counterintuitive approach to living a good life.</li>
<li><strong><a href="https://www.goodreads.com/book/show/18144590-the-alchemist" target="_blank" rel="noopener noreffer">The Alchemist</a></strong> (Paulo Coelho): Beautiful fable about following your dreams.</li>
<li><strong><a href="https://www.goodreads.com/book/show/22668729-hooked" target="_blank" rel="noopener noreffer">Hooked</a></strong> (Nir Eyal): How to build habit-forming products. Essential for product designers.</li>
<li><strong><a href="https://www.goodreads.com/book/show/38121309-the-startup-way" target="_blank" rel="noopener noreffer">The Startup Way</a></strong> (Eric Ries): How modern companies use entrepreneurial management.</li>
<li><strong><a href="https://www.goodreads.com/book/show/368593.The_4_Hour_Workweek" target="_blank" rel="noopener noreffer">The 4-Hour Work Week</a></strong> (Tim Ferriss): A game changer that questions conventional work structures.</li>
</ul>
<h2 id="further-reads">Further Reads</h2>
<ul>
<li><a href="https://www.ssp.sh/brain/reading-books-for-a-happy-life/" target="_blank" rel="noopener noreffer">Reading Books for a Happy Life</a>: My thoughts on why reading books matters.</li>
<li><strong><a href="https://www.ssp.sh/brain/audiobooks/" target="_blank" rel="noopener noreffer">Audiobooks</a></strong>: I listen to most of my books as audiobooks. It works better for me and my brain, as I read all day long already. Books read by the authors on Audible are just another level. It feels like listening to them in person. Although a physical book has advantages, audiobooks are just something different. No wonder they are ever-growing.</li>
<li><strong><a href="/writers-room/" rel="">Writer&rsquo;s Room</a></strong>: Where I write and update on latest insight in my design process.</li>
</ul>
<h3 id="other-recommendations">Other Recommendations</h3>
<ul>
<li><a href="https://sive.rs/book" target="_blank" rel="noopener noreffer">Derek Sivers book notes</a></li>
</ul>
<hr>
<p><em>Last updated: December 30, 2025</em></p>
]]></description>
</item>
<item>
    <title>The Act and the Outcome of Creation</title>
    <link>https://www.ssp.sh/blog/on-creation/</link>
    <pubDate>Thu, 16 Jul 2026 08:31:17 &#43;0100</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/on-creation/</guid><enclosure url="https://www.ssp.sh/blog/on-creation/featured-image.jpg" type="image/jpeg" length="0" /><description><![CDATA[<p>Creation is the ultimate form of pursuing ourselves, giving to the world when shared, and using the power of our subconscious. It gives us joy, and to every artist, it is the ultimate (flow) state of happiness.</p>
<h2 id="the-act-of-creation">The Act of Creation</h2>
<p>The act of creation is an outlet. It gives joy to us when we create something out of nothing, we block out anxiety or boredom.</p>
<p>Creating should be done like:</p>
<ul>
<li><strong>a kid in mind</strong>: effortless, exploring your thoughts, and seeing where it leaves you, as Picasso said: He wouldn&rsquo;t have bothered to start a painting if he knew the outcome.</li>
<li><strong>a play</strong><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>: flawless and free.</li>
<li><strong>calm like the water</strong>: flowing wherever your mind is going.</li>
<li><strong>to fulfill yourself</strong>: by portraying your never-resting thoughts. Share it with the world. Create with joy and love in mind.</li>
<li><strong>follow your train of thought</strong>: see where it leads you. You start somewhere, on an unfinished emotion, a task, or anything to ponder on.</li>
</ul>
<p>Creation, and especially writing, can help <em>resolve the unresolved</em>.</p>
<h3 id="creation-to-make-a-difference-joy-and-laughter">Creation to Make a Difference: Joy and Laughter</h3>
<p>Creating is also about <strong>making a difference</strong>. If nothing changes in you, the people, not enough value was added to your creation. Any creation should trigger something. But foremost, the process of creation should change something in you, some release, a good feeling, it must feel right. If it does nothing, an empty feeling, the creation might not be there yet.</p>
<p>Create with <strong>love and empathy</strong> in mind. Liking someone is one thing, but giving love, caring for someone, and integrating that into your work is the ultimate way to create.</p>
<p>Create <strong>joy and laughter</strong>. Speak the language of people, make them happy by creating enjoyment.</p>
<h3 id="following-our-emotions-without-boundaries">Following Our Emotions without Boundaries</h3>
<p>When we follow our emotions <strong>without any boundaries</strong>, following our instincts, we get into a state of deep focus and deep concentration where all thoughts can be resolved.</p>
<p>Creation is our inner [[Gut Feeling|Instinct]]. If we follow and nourish it, great things can arise from it. Treat it as an outlet for your body and mind. Go with the flow, follow along. Create for the sake of creation.</p>













  
<figure><span class="img-expandable" data-full="/blog/on-creation/creation-fountain-pen.webp">
<a target="_blank" href="/blog/on-creation/creation-fountain-pen.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">My outlet of writing this article, not knowing where it leads me, but just letting my thoughts out as they come, while sitting still in nature somewhere close to the beach in Italy.</figcaption>
</figure>
<h3 id="learn-for-life-follow-your-own-uniqueness">Learn for Life, Follow Your Own Uniqueness</h3>
<p>The act of creation is <strong>following your own life rhythm</strong>, for example, the [[Pathless Path]]. It is the ultimate form of connecting with yourself.</p>
<p>All creation is unique, unique specifically to you as the creator, but also (hopefully) to the reader, viewer, or consumer of your work. Your creations live long after your death; others will connect and create new works from them. It&rsquo;s giving joy after you&rsquo;re gone.</p>
<p><strong>Learn for Life</strong>, as I like to say, has been my earliest mantra when starting any of my creations shared online. The common definition of <em>success</em> is reaching a desired outcome. The Scientist&rsquo;s definition of success is: if you learn something new, you haven&rsquo;t failed, as Anne-Laure Le Cunff says on <a href="https://share.snipd.com/snip/c0b1451b-d924-42ce-8c8a-c8fc559d3925" target="_blank" rel="noopener noreffer">How to Design Tiny Experiments Like a Scientist</a>.</p>
<p>For example, a scientist defines it like this: if you say you want to write more, instead of just writing, you define &lsquo;I will write for at least one month, or at least 10 articles. And then you don&rsquo;t stop before that. Success is the experience, not the outcome.</p>
<h2 id="the-outcome-of-creation">The Outcome of Creation</h2>
<p>The act of creation is a <strong>slow process</strong> that needs time and experience. A slow life, an [[The Ordinary (Boring) Life|ordinary, boring life]] even, to focus on the details, using the craftsmanship refined over the years, and retrieving joy from the process of creation.</p>
<p>What matters is the quality and outcome we are proud to release to the world.</p>
<h3 id="gifts-shared-with-the-world">Gifts Shared with the World</h3>
<p>You create for yourself with <strong>no return in mind</strong>, you just share it as a gift for the world, for anyone to consume.</p>
<p>Creation thought of as a gift is an easy way for you to create without the burden of pleasing people, as it&rsquo;s take-it-or-leave-it, like a gift. No strings attached, just <strong>making gifts</strong>.</p>
<h3 id="a-curious-mind-is-mostly-a-subconscious-mind">A Curious Mind is Mostly a Subconscious Mind</h3>
<p>When we create, most of our <strong>subconscious is driving</strong> the thought. Just let it cruise and see what the outcome is. Use the conscious mind for fixing errors and making sure the sentences make sense later, but don&rsquo;t start with it.</p>













  
<figure><span class="img-expandable" data-full="/blog/on-creation/conscois-vs-subconcious.webp">
<a target="_blank" href="/blog/on-creation/conscois-vs-subconcious.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Conscious mind vs subconscious and unconscious minds (5 to 95% difference) | Image from <a href="https://twitter.com/connibiesalski/status/1641821412238442496" target="_blank" rel="noopener noreffer">this Tweet</a>.</figcaption>
</figure>
<h3 id="the-spark-of-joy-philosophy">The Spark of Joy Philosophy</h3>
<p>Different flairs for designs to add ease or user delight to your creation. Similar to the &ldquo;<a href="https://github.com/swyxio/spark-joy" target="_blank" rel="noopener noreffer">Spark Joy Philosophy</a>&rdquo;, which is very fitting when creating for the enjoyment of the reader.</p>
<p>To me, creation <strong>sparks joy for myself</strong>, it&rsquo;s the outlet for me to release and <strong>organize my thoughts</strong>. It avoids the [[shallow happiness]] that I get from social media, binge-watching Netflix, or other brainless activities on the phone or TV. Sure, there&rsquo;s time for that too, to calm down, but if we are not careful, <a href="/blog/well-being-algorithms/" rel="">the algorithms</a> will take over and we default to always choosing the easy choice.</p>
<h2 id="the-reward-is-long-term">The Reward is Long-term</h2>
<p>Creating is hard, there&rsquo;s friction, it does not directly work, but the <strong>reward is long-term</strong>, with [[deep happiness]] and a deep flow state: &ldquo;It&rsquo;s magical that they just tried to be there&rdquo;, which is the highest form of happiness artists get (see <a href="https://www.goodreads.com/book/show/615570.The_Artist_s_Way" target="_blank" rel="noopener noreffer">The Artist&rsquo;s Way</a>) as we discovered in <a href="/blog/finding-flow/" rel="">Finding Flow</a>, with escaping digital distractions through deep work and slow living.</p>
<p>I hope you find your outlet for creation, an act you can partake in to develop happiness and joy for yourself, getting into that deep flow and having a slow process where many small gifts of creation can be shared with the world.</p>
<p><em>This article was created with the inspiration of reading <a href="https://sive.rs/h" target="_blank" rel="noopener noreffer">How to Live</a> by Derek Sivers in Cavallino, Italy.</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I was watching kids play football on a campsite football field in Italy, so free and joyful.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
<item>
    <title>The Grammar of Data: Define Once, Run Anywhere with Cross-Engine Expressions</title>
    <link>https://www.ssp.sh/blog/grammar-data-engineering/</link>
    <pubDate>Wed, 08 Jul 2026 08:00:08 &#43;0200</pubDate>
    <author>Simon Späti</author>
    <guid>https://www.ssp.sh/blog/grammar-data-engineering/</guid><enclosure url="https://www.ssp.sh/blog/grammar-data-engineering/featured-image.png" type="image/png" length="0" /><description><![CDATA[<p>Grammars for languages or any other field are a beautiful thing. They compress complex systems into a language with a couple of rules. For the spoken language example, we know when to capitalize a letter or how to start a sentence. There are clear rules. Grammars also help us remember, as we do not need to recall every little rule, but apply them in a structured way.</p>
<p>For text editing, we have Vim motions that help us navigate a text document with 1000s of shortcuts, but because there is a grammar, we do not need to remember them all, but learn the structure of the grammar and combine them. But what if you work in data? What if we could have the same for data, a grammar for data engineering, or a language that defines it?</p>
<p><em>Expressing</em> our needs declaratively and decisively? Also, expressing it in a way that leads to reproducible outcomes, or works with multiple parts and execution engines already out there. This is what we will discuss in this article. How existing tooling, such as Ibis, provides some capabilities, and how xorq extends them by adding full lineage and transparency for humans, with included executable memory for useful tabular data, all manifested in a single git repository.</p>
<h2 id="expressions-for-data-engineering-workloads">Expressions for Data Engineering Workloads</h2>
<p>Having a <strong>grammar for data engineering</strong> means we can express the workloads in a <a href="https://www.ssp.sh/brain/declarative/" target="_blank" rel="noopener noreffer">declarative</a> manner, and then be sure we can deterministically reproduce and apply that exact definition.</p>
<p>It&rsquo;s similar to the concept of a <a href="https://www.rilldata.com/blog/the-rise-of-the-declarative-data-stack" target="_blank" rel="noopener noreffer">Declarative Data Stack</a> I introduced a while back, but it gives the stack not only configurations but also a language with in-built manifestation and execution engines.</p>













  
<figure><span class="img-expandable" data-full="/blog/grammar-data-engineering/write-manifest-execute.webp">
<a target="_blank" href="/blog/grammar-data-engineering/write-manifest-execute.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Write -&gt; Manifest and Execute | Image from <a href="https://github.com/xorq-labs/xorq" target="_blank" rel="noopener noreffer">Composable expressions for data pipelines</a></figcaption>
</figure>
<p>In the above image, we see:</p>
<ol>
<li>How to <strong>express</strong> (write) our transformations and business logic. It&rsquo;s the context of every ML or DE pipeline.</li>
<li>We can build the <a href="https://xorq.dev/blog/expression-lifecycle/" target="_blank" rel="noopener noreffer">expression</a> into a <strong>manifest</strong> that has a unique hash, runs input validations, tracks lineage, creates a deterministic cache, and produces a human-readable <code>expr.yaml</code> you can diff and review in a PR.</li>
<li>Lastly, we can <strong>execute</strong> it in any execution engine with the <em>same manifest</em>.</li>
</ol>
<p>This is hugely powerful and separates the concerns of defining logic, verification in the manifest step, and execution as a composable data stack, as Wes McKinney <a href="https://open.spotify.com/episode/4yEBsHs75QyxnQqK11ghyC?si=2c7861fde2354a52" target="_blank" rel="noopener noreffer">called it</a>, with multi-compute engine possibilities.</p>
<h3 id="how-the-de-language-works-different-expression-types">How the DE Language Works: Different Expression Types</h3>
<p>Every grammar starts with nouns, and here the noun is the <strong>source</strong>, a node that holds data but carries no transformation yet. It might be an in-memory table, a registered connection to a warehouse, or just a lazy pointer to a file on disk that hasn&rsquo;t been read. They&rsquo;re simply <em>referenced</em>, the way a noun refers to a thing before any verb acts on it.</p>
<p>The verbs in our language are <strong>transforms</strong> such as <em>filter, select, mutate, aggregate, join, order, limit</em>. Each one takes a source (or another transformed expression) and returns a new, immutable expression. You do not mutate anything before it, only describe what should happen next.</p>
<p>Looking at a definition such as <code>.filter(...).aggregate(...).mutate(...)</code>, we can see this as a sentence. The moment a verb is applied, the expression stops being a plain noun and becomes a <em>statement</em>, a description of &ldquo;data plus what should happen to it.&rdquo; But the sentence isn&rsquo;t spoken yet, it stays inert, fully composed but unexecuted, until something finally asks it to run. That&rsquo;s the deferred part of the grammar: writing the sentence and saying it out loud are two different acts.</p>
<p>There&rsquo;s a third part of speech worth naming: the <strong>template</strong>. Instead of writing a sentence about a specific noun, you can write one about a noun&rsquo;s <em>shape</em>, a schema with no rows behind it. A template says &ldquo;given something with a column of this type, here is what I&rsquo;ll do to it,&rdquo; and only later gets bound to an actual source, at which point the placeholder resolves and it becomes an ordinary statement again.</p>
<p>And we have <strong>modifiers</strong> that ride alongside a statement without changing what it computes. They&rsquo;re small tags of metadata that say &ldquo;this expression also represents a fitted model&rdquo; or &ldquo;this is a saved reference to something else.&rdquo; It&rsquo;s like a footnote with additional metadata that doesn&rsquo;t change the surface meaning, but adds context for later use.</p>
<p>This analogy makes the grammar compose the same way regardless of which engine eventually executes it. There are more parts, but with just these four, noun, verb, template, modifier, you can read (and write) arbitrarily complex data pipelines the same way learning a handful of verb-and-object combinations in a text editor lets you compose arbitrarily complex edits.</p>
<blockquote>
<p>[!tip] Avoids building &ldquo;Inner-Platform Effect&rdquo; with repeated tools<br>
With this grammar, we can avoid repeatedly implementing the same logic we already have, but manifest and express our logic once, and reuse it with different execution engines, exactly what Ibis and xorq allow. Similar to what the <a href="https://en.wikipedia.org/wiki/Inner-platform_effect" target="_blank" rel="noopener noreffer">inner-platform effect</a> means for software best practices.</p>
</blockquote>
<h3 id="why-a-grammar-is-really-good-for-llms">Why a Grammar is Really Good for LLMs</h3>
<p>Having a grammar is really good for LLMs, too. It helps them first to declare data artifacts and second to execute them reproducibly.</p>
<p>On top, expressions can be LLM-agnostic, and we can interchange the LLMs we use just with an expression. Also, the chart is just an expression, or the data catalog and the metrics.</p>
<h2 id="model-once-represent-everywhere-expressing-the-full-data-stack-with-a-single-expression">Model Once, Represent Everywhere: Expressing the Full Data Stack with a Single Expression</h2>
<p>Like <a href="https://netflixtechblog.com/uda-unified-data-architecture-6a6aee261d8d" target="_blank" rel="noopener noreffer">UDA (Unified Data Architecture)</a> from Netflix, we define our expressions once and represent them everywhere. Netflix built UDA to solve <strong>duplicated models, inconsistent terminology, and siloed systems</strong>, where the same concept like &lsquo;actor&rsquo; or &lsquo;movie&rsquo; gets modeled differently across teams, with no shared foundation. Their answer was a full knowledge graph with a metamodel, making the conceptual model part of the actual control plane.</p>
<p>Not everyone needs Netflix-scale tooling, though. For a code-first approach, xorq gives you the same core principle: define once, execute anywhere by writing a declarative Ibis expression, serializing them as content-addressed YAML artifacts, and running against any supported engine, fully reproducible.</p>
<p>The difference worth noting: UDA is a <em>semantic</em> layer defining what data <em>means</em> across systems. Xorq is a <em>computational</em> layer defining what transformations <em>do</em> across engines. Both reject the same anti-pattern of re-implementing the same logic for every system.</p>
<h2 id="entering-xorq-the-horizontal-data-architecture">Entering Xorq: The Horizontal Data Architecture</h2>
<p>Xorq is an executable memory system for tabular data that works horizontally across your data stack, supporting everything from discovery with a catalog to defining transformation logic to modeling.</p>
<p>It has declarative transformation (Pandas style), and you can build ML pipelines and prepare data with its semantics in a single stack that is not vertically integrated, but horizontally integrated, giving your agents a catalog of executable pipelines and turning short-lived agent work such as wrangling scripts, sklearn pipelines, ad-hoc tables into durable, composable, executable artifacts that any future agent or human can discover, reproduce, and reuse.</p>













  
<figure><span class="img-expandable" data-full="/blog/grammar-data-engineering/horizontal-data-stack.webp">
<a target="_blank" href="/blog/grammar-data-engineering/horizontal-data-stack.webp" title="Open image in full size">

</a><button type="button" class="expand-btn" aria-label="Expand image" title="Expand image" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></button>
</span><figcaption class="image-caption">Old vertical siloed way vs. the horizontal composable data stack way with multi-engine</figcaption>
</figure>
<p>The horizontal data stack shows what Xorq brings to the table. <strong>Xorq&rsquo;s origins</strong> started from a <a href="https://xorq.dev/blog/bsl-xorq/" target="_blank" rel="noopener noreffer">git-native semantic layer</a>, for data analysts out of college, to build semantic models for a living, to make their lives easier.</p>
<p>From point-and-click tools, dragging tables and drawing joins manually, only to add more reporting tools on top to create pixel-perfect reports. Also performance-wise, it didn&rsquo;t scale, meaning we needed cubes to make it faster, adding another layer of complexity.</p>
<p>And there was no lineage that shows from source to dashboard. The question asked was: &ldquo;what if we could do this <strong>end-to-end data engineering workflow locally</strong>?&rdquo;. This is what the horizontal data stack and xorq are providing.</p>
<p>To add semantic layer capabilities, Julien Hurault and Hussain built the <a href="https://github.com/boringdata/boring-semantic-layer" target="_blank" rel="noopener noreffer">Boring Semantic Layer</a> + the Xorq catalog, providing a semantic model you define in Python, check into git, and query from the CLI.</p>
<h3 id="compressing-logic-into-a-single-executable">Compressing Logic into a Single Executable</h3>
<p>Compression of a full data stack into a single executable is hard, but xorq tries exactly this with the help of Ibis, git, uv, and DataFusion.</p>
<p>The <strong>design choices</strong> of xorq showcase even better what it is, and what they enable:</p>
<ul>
<li><strong><a href="https://ibis-project.org/" target="_blank" rel="noopener noreffer">Ibis</a> as expression layer</strong> (v9.5.0+, partial): Declarative dataframe expressions compiled to multiple backends (xorq supports a subset of the Ibis API, not the full surface)</li>
<li><strong><a href="https://git-scm.com/" target="_blank" rel="noopener noreffer">Git</a> for state and storage</strong>: The catalog is a git repo of entries with git-annex support for large files</li>
<li><strong><a href="https://docs.astral.sh/uv/" target="_blank" rel="noopener noreffer">uv</a> for reproducible environments</strong>: Each entry ships with a wheel and pinned <code>requirements.txt</code>.</li>
<li><strong><a href="https://datafusion.apache.org/" target="_blank" rel="noopener noreffer">DataFusion</a> for embedded compute</strong>: Pipelines execute in-process with SQL and UDFs</li>
</ul>
<h3 id="composable-data-engines">Composable Data Engines</h3>
<p>Another big advantage of expressions and having a grammar for data engineering is easily <a href="https://docs.xorq.dev/getting_started/switch_backends.html" target="_blank" rel="noopener noreffer">switching between backends</a>, with no change to the transformation or business logic. It&rsquo;s just defining the backend from Apache Arrow Flight to DuckDB or any other engine.</p>
<p>We write the definitions and express our tabular data and computations. The engine, in this case xorq, can build it into a manifest file that is deterministic and hashed.</p>
<p>Xorq uses Ibis as the expression layer for single-backend logic, then builds the <strong>cross-engine expression tree</strong> into a serialized YAML artifact. When moving data between backends, xorq transfers Apache Arrow <a href="https://docs.rs/arrow/latest/arrow/record_batch/struct.RecordBatch.html" target="_blank" rel="noopener noreffer">RecordBatch</a> streams between them—each backend acts as a RecordBatch transducer. No CSV serialization, no JSON encoding needed. This makes backend switching fast and memory-efficient. Write declarative Ibis expressions that run like a tool—xorq extends Ibis with caching, <a href="https://docs.xorq.dev/concepts/understanding_xorq/multi_engine_execution.html" target="_blank" rel="noopener noreffer">multi-engine execution</a>, and UDFs.</p>
<p>Here&rsquo;s an example of using DuckDB and Postgres in conjunction:</p>
<div class="highlight"><div class="chroma"><div class="code-header language-python"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">xorq.api</span> <span class="k">as</span> <span class="nn">xo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Connect to engines</span>
</span></span><span class="line"><span class="cl"><span class="n">pg</span> <span class="o">=</span> <span class="n">xo</span><span class="o">.</span><span class="n">postgres</span><span class="o">.</span><span class="n">connect_env</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">db</span> <span class="o">=</span> <span class="n">xo</span><span class="o">.</span><span class="n">duckdb</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Load data from different sources</span>
</span></span><span class="line"><span class="cl"><span class="n">batting</span> <span class="o">=</span> <span class="n">pg</span><span class="o">.</span><span class="n">table</span><span class="p">(</span><span class="s2">&#34;batting&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">awards</span> <span class="o">=</span> <span class="n">xo</span><span class="o">.</span><span class="n">examples</span><span class="o">.</span><span class="n">awards_players</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">backend</span><span class="o">=</span><span class="n">db</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Filter in respective engines</span>
</span></span><span class="line"><span class="cl"><span class="n">recent</span> <span class="o">=</span> <span class="n">batting</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">batting</span><span class="o">.</span><span class="n">yearID</span> <span class="o">==</span> <span class="mi">2015</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">nl_awards</span> <span class="o">=</span> <span class="n">awards</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">awards</span><span class="o">.</span><span class="n">lgID</span> <span class="o">==</span> <span class="s2">&#34;NL&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Move data to postgres for join</span>
</span></span><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">recent</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">nl_awards</span><span class="o">.</span><span class="n">into_backend</span><span class="p">(</span><span class="n">pg</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s2">&#34;playerID&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">result</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p><em>Move data between different engines within a single expression using <code>into_backend()</code>, here Postgres and DuckDB</em></p>
<p>You can see <strong>how easily you choose your most optimized execution engine</strong>, whether in the above example choosing DuckDB for filtering recent batting and using Postgres to filter NL (National League) awards, and joining the two with the Postgres engine.</p>
<p>Engines supported by xorq as of now, with the ability to move data between them, are (check <a href="https://docs.xorq.dev/api_reference/backends/supported_backends.html" target="_blank" rel="noopener noreffer">Supported backends</a> for the latest):</p>
<ul>
<li><strong>Embedded</strong>: DataFusion, DuckDB, SQLite, Pandas</li>
<li><strong>Warehouses</strong>: Snowflake, Databricks, Trino, Postgres</li>
<li><strong>Lakehouse</strong>: PyIceberg</li>
<li><strong>Arrow Flight</strong>: GizmoSQL (DuckDB over Arrow Flight SQL)</li>
</ul>
<h4 id="cross-engine-expression-tree">Cross-Engine Expression Tree</h4>
<p>With different engines supported, we can use the compressed single executable logic across engines. We can build <a href="https://docs.xorq.dev/getting_started/your_first_expression.html" target="_blank" rel="noopener noreffer">expression graphs</a> before executing them, which works like this, with <strong><a href="https://github.com/xorq-labs/xorq#one-expression-many-engines" target="_blank" rel="noopener noreffer">one expression, many engines</a></strong>:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-python"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">expr</span> <span class="o">=</span> <span class="n">penguins</span><span class="o">.</span><span class="n">into_backend</span><span class="p">(</span><span class="n">xo</span><span class="o">.</span><span class="n">sqlite</span><span class="o">.</span><span class="n">connect</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="n">expr</span><span class="o">.</span><span class="n">ls</span><span class="o">.</span><span class="n">backends</span></span></span></code></pre></td></tr></table>
</div></div></div>
<p>The output of building a cross-engine expression is a <strong>directory containing your serialized pipeline</strong> with a unique hash identifying each build and its artifacts and expressions. When executed, the output is the resulting object or data.</p>
<p>And the <strong>expressions are tools, <a href="https://github.com/xorq-labs/xorq#expressions-are-tools-arrow-is-the-pipe" target="_blank" rel="noopener noreffer">Arrow is the pipe</a></strong>. E.g., a Unix pipe streams text between small programs. Xorq pipes Arrow streams between expressions:  <code>unix : programs :: xorq : arrow-transforms</code></p>
<p>That executes like this:</p>
<div class="highlight"><div class="chroma open"><div class="code-header language-sh"><span class="code-title"><i class="arrow svg-icon icon-code-right"></i></span><span class="ellipses"><i class="svg-icon icon-ellipsis"></i></span><span class="copy" title="Copy to clipboard"><i class="svg-icon icon-copy"></i></span></div><div class="table-wrapper">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">In <span class="o">[</span>6<span class="o">]</span>: expr.to_pyarrow_batches<span class="o">()</span>
</span></span><span class="line"><span class="cl">Out<span class="o">[</span>6<span class="o">]</span>: &lt;pyarrow.lib.RecordBatchReader at 0x15dc3f570&gt;</span></span></code></pre></td></tr></table>
</div></div></div>
<p>This is quite short and potentially abstract to understand when never used, but we will go into more examples and details in another article.</p>
<h2 id="a-shared-language-for-data">A Shared Language for Data</h2>
<p>This article introduces a new way of describing data transformations for machine learning or data engineering pipelines in a direct and simple way that works locally with any execution engine, without changing the code itself.</p>
<p>It&rsquo;s a good place if you <strong>need a trusted harness</strong> for a data engineering persona. We can define once and use it with the engine that works best for your workload and data engineering environment.</p>
<p>We had a look at how we <code>write -&gt; manifest -&gt; execute</code> with xorq, its advantages, and why you might use it for modeling once and representing everywhere. By adding AI agents to the mix, which help us pull the right lever, instead of bigger, more expensive models or more tokens, we improve accuracy with more semantic understanding, with a grammar the model can learn and apply, even pre-manifest before execution, and run them deterministically every time. This is a huge addition to working just with agentic Skills files that are free-form Markdown and pull data all over, or are not defined precisely enough. It&rsquo;s all about having high-quality context in the right format, with a clear definition where humans and AI agents can interchange and help each other.</p>
<p>There&rsquo;s a lot more to come, with showcasing the horizontal data stack and the use cases it supports, how we build expressions versus running computations, and how data catalogs are integrated into the picture, too.</p>
<p>&ndash;</p>
<p>Check out <a href="https://github.com/xorq-labs/xorq" target="_blank" rel="noopener noreffer">xorq code</a> and star it on GitHub, or read more behind the scenes at <a href="https://docs.xorq.dev/" target="_blank" rel="noopener noreffer">Xorq documentation</a>.</p>
<p>They also have a macOS desktop app coming up that does it all in one unified app, geared towards non-technical users. <a href="https://xorq.dev/" target="_blank" rel="noopener noreffer">Join the waitlist</a> for that.</p>
<hr>
<pre class=""><em>Full article published at <a href="https://xorq.dev/blog/grammar-for-data-engineering/" target="_blank" rel="noopener noreferrer">xorq.dev</a> - written as part of <a href="/services/">my services</a></em></pre> 
]]></description>
</item>
</channel>
</rss>
