<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Alexey On Data]]></title><description><![CDATA[Practical writing on building, testing, and operating AI systems, workflows, and automation, based on my projects and experiments. Subscribe to get ideas you can reuse in your own work.]]></description><link>https://aishippingblog.com</link><image><url>https://substackcdn.com/image/fetch/$s_!otON!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png</url><title>Alexey On Data</title><link>https://aishippingblog.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 05 Oct 2026 11:27:58 GMT</lastBuildDate><atom:link href="https://aishippingblog.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Alexey Grigorev]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[alexeyondata@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[alexeyondata@substack.com]]></itunes:email><itunes:name><![CDATA[Alexey Grigorev]]></itunes:name></itunes:owner><itunes:author><![CDATA[Alexey Grigorev]]></itunes:author><googleplay:owner><![CDATA[alexeyondata@substack.com]]></googleplay:owner><googleplay:email><![CDATA[alexeyondata@substack.com]]></googleplay:email><googleplay:author><![CDATA[Alexey Grigorev]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Aplexer - Why I Stopped Using Tmux and Created My Own Terminal Multiplexer]]></title><description><![CDATA[Advantages and disadvantages of tmux and my new agent multiplexer]]></description><link>https://aishippingblog.com/p/aplexer-why-i-stopped-using-tmux</link><guid isPermaLink="false">https://aishippingblog.com/p/aplexer-why-i-stopped-using-tmux</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 02 Oct 2026 13:21:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!x_Gi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>tmux is a terminal multiplexer that I use to run agents on a remote machine. I wrote about using it in my article <a href="https://aishippingblog.com/p/the-system-i-built-to-ship-code-from">The System I Built to Ship Code From a Phone</a>.</p><p>When you connect to a remote machine with SSH, the processes you launch are bound to your SSH session. That means when you disconnect, or the connection drops, the processes die together with the session.</p><p>A terminal multiplexer is a tool that detaches the processes you start from the SSH session, so they can keep running after the disconnect. Screen and tmux are the most well-known terminal multiplexers, but tmux is more modern and generally preferred among developers.</p><p>I&#8217;ve used tmux for ages, but with agents running 24/7 on my remote dev box, it no longer works for me. To replace it, I created <a href="https://github.com/PocketShell-io/aplexer">aplexer</a>. In this article I&#8217;ll tell you why.</p><p>In particular, we&#8217;ll cover:</p><ul><li><p>What tmux is and how it works</p></li><li><p>What&#8217;s PTY and how it&#8217;s related to SSH connections </p></li><li><p>OOM killers and cgroups</p></li><li><p>Aplexer as the solution to my current problems with tmux</p></li></ul><p>Let&#8217;s start!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Terminal multiplexer</strong></h2><p>The <a href="https://github.com/tmux/tmux">tmux README</a> says:</p><blockquote><p>tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached.</p></blockquote><p>It&#8217;s a layer between you (your SSH session) and the processes you run.</p><p>After I ssh into my dev box, I can start a tmux session:</p><pre><code>tmux new-session -s ai-shipping-labs</code></pre><p>Under the hood, multiple things happen:</p><ul><li><p>My terminal starts a tmux client process</p></li><li><p>The client connects to a tmux server</p></li><li><p>If the server is not running, the command starts it</p></li></ul><p>The client connects to the server through a socket. The server creates a session and a PTY for that session.</p><p>A PTY is a pseudo-terminal created by the kernel. It has two sides:</p><ul><li><p>the master (user side) - sends the input from the user to whatever is running in the terminal.</p></li><li><p>the slave (shell side) - receives the input from the master and sends it to the shell or whatever program is running in the terminal.</p></li></ul><p>When we start the standard terminal emulator app on any Linux, it creates a PTY. The master side connects to the terminal app, and the slave side connects to a shell (usually bash). It&#8217;s a bit more complicated than that but I won&#8217;t go into more details here.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S1Zy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S1Zy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 424w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 848w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 1272w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S1Zy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png" width="1080" height="158" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:158,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35310,&quot;alt&quot;:&quot;A terminal emulator connects to the master end of one kernel PTY while Bash connects to the slave end&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A terminal emulator connects to the master end of one kernel PTY while Bash connects to the slave end" title="A terminal emulator connects to the master end of one kernel PTY while Bash connects to the slave end" srcset="https://substackcdn.com/image/fetch/$s_!S1Zy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 424w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 848w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 1272w, https://substackcdn.com/image/fetch/$s_!S1Zy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4d78914-bb3e-4836-adc5-9d0339896570_1080x158.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">The terminal emulator uses the master end, and Bash uses the slave end.</figcaption></figure></div><p>When we start a process from a PTY, it gets attached to the shell. When the PTY stops, the shell stops too, and all the connected processes follow.</p><p>If we take the terminal app in Ubuntu, each tab in the app is a PTY. When I start a process in a tab, and then close that tab, the PTY closes too, and the process follows.</p><p>A similar thing happens when we use SSH. The SSH client on our computer connects to the SSH server (sshd) on the remote machine, and sshd creates a PTY. The master is on the sshd side, and the slave is on the shell side.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ngJ1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ngJ1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 424w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 848w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ngJ1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png" width="1180" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:1180,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;An SSH connection crosses from an SSH client on the local computer to sshd on the remote devbox, where sshd opens a separate PTY for Bash&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An SSH connection crosses from an SSH client on the local computer to sshd on the remote devbox, where sshd opens a separate PTY for Bash" title="An SSH connection crosses from an SSH client on the local computer to sshd on the remote devbox, where sshd opens a separate PTY for Bash" srcset="https://substackcdn.com/image/fetch/$s_!ngJ1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 424w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 848w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ngJ1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7795d97-25e7-40dc-8cb4-2443d332417d_1180x400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">SSH uses a separate PTY on the remote machine.</figcaption></figure></div><p>When SSH disconnects, sshd stops the PTY that was created for this session, and all the processes die with it.</p><p>Terminal multiplexers add one more hop: tmux server creates another PTY, and the shell that&#8217;s connected to that PTY is the parent for all these processes.</p><p>That&#8217;s why the processes that we start in tmux continue running - they are attached to tmux server&#8217;s PTY. Only the tmux client dies when the SSH connection drops.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mvjM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mvjM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 424w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 848w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 1272w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mvjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png" width="728" height="300.44444444444446" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:520,&quot;width&quot;:1260,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;After an SSH disconnect, sshd, its PTY, and the tmux client stop while the tmux server keeps its pane PTY and agent running&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="After an SSH disconnect, sshd, its PTY, and the tmux client stop while the tmux server keeps its pane PTY and agent running" title="After an SSH disconnect, sshd, its PTY, and the tmux client stop while the tmux server keeps its pane PTY and agent running" srcset="https://substackcdn.com/image/fetch/$s_!mvjM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 424w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 848w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 1272w, https://substackcdn.com/image/fetch/$s_!mvjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b250a24-590c-4761-ad89-b46c5683b51a_1260x520.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">After SSH disconnects, the tmux client exits. The server keeps the agent&#8217;s PTY open.</figcaption></figure></div><p>There are multiple important organizational primitives in tmux:</p><ul><li><p>A pane is the basic unit of tmux. Each pane has its own PTY.</p></li><li><p>A window has one or multiple panes.</p></li><li><p>A session holds one or multiple windows.</p></li><li><p>The tmux server can have many sessions.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6SC2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6SC2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 424w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 848w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 1272w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6SC2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png" width="728" height="419.6111111111111" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:830,&quot;width&quot;:1440,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A tmux window with three panes: htop monitors the devbox, an agent session runs in the top-right pane, and a shell lists other sessions in the bottom pane&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A tmux window with three panes: htop monitors the devbox, an agent session runs in the top-right pane, and a shell lists other sessions in the bottom pane" title="A tmux window with three panes: htop monitors the devbox, an agent session runs in the top-right pane, and a shell lists other sessions in the bottom pane" srcset="https://substackcdn.com/image/fetch/$s_!6SC2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 424w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 848w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 1272w, https://substackcdn.com/image/fetch/$s_!6SC2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F788a4636-7a06-47fc-bea4-f488088dd680_1440x830.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">One tmux window with 3 panes: htop, claude and shell.</figcaption></figure></div><p>When I create a new session with a command like this:</p><pre><code>tmux new-session -s ai-shipping-labs</code></pre><p>It creates a session with one window with one pane inside it, and this pane has a PTY. I can add a new window or split the current one horizontally or vertically into multiple panes, and each pane would be a separate PTY.</p><p>I usually don&#8217;t do that, though. I use tmux mostly for keeping my agents running when I disconnect, so I don&#8217;t need any window arrangement capabilities. For me it&#8217;s always one session - one PTY, and different agents are running in different sessions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gzfx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gzfx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 424w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 848w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 1272w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gzfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png" width="1040" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:1040,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;One tmux server fans out to three sessions, each with one pane, one PTY, and an agent or shell&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="One tmux server fans out to three sessions, each with one pane, one PTY, and an agent or shell" title="One tmux server fans out to three sessions, each with one pane, one PTY, and an agent or shell" srcset="https://substackcdn.com/image/fetch/$s_!gzfx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 424w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 848w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 1272w, https://substackcdn.com/image/fetch/$s_!gzfx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13e54fac-d62a-4a50-88b8-bbe662997690_1040x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">I use one pane per session, so each agent gets its own PTY.</figcaption></figure></div><p>We can do a lot of things programmatically with tmux. We can create all these windows and panes using the tmux CLI, or we can send input to any of the panes as if it was typed by a human. In fact, the earlier screenshot with three panes was created by <a href="https://gist.github.com/alexeygrigorev/8df0400d8814914291487ada31b4a119">this script</a>.</p><p>Because of that, with a bit of scripting, you can teach agents running in different tmux sessions to talk to each other.</p><h2><strong>Problems with tmux</strong></h2><p>I like tmux, but as I started running more and more agents, session management became more difficult.</p><p>I really struggle with the CLI. The commands are:</p><pre><code>tmux new-session -s ai-shipping-labs
tmux list-sessions
tmux attach-session -t ai-shipping-labs</code></pre><p>I always forget these commands. Typing all that, even with autocomplete, is always complicated. I never seem to remember what to type and need to look it up.</p><p>Also, you have to remember that <code>-s</code> is for new session and <code>-t</code> is for attach. To make it even more confusing, <code>new-session</code> also has the <code>-t</code> parameter, but it&#8217;s not the same as <code>-s</code>.</p><p>Eventually I solved this problem with <a href="https://github.com/alexeygrigorev/tmuxctl">tmuxctl</a> - a wrapper around tmux.</p><p>I created an executable <code>tmuxctl</code> and an alias <code>t</code> for it to save typing time. With it, I can simply run</p><pre><code>t -</code></pre><p>It will:</p><ul><li><p>create a tmux session in the current directory</p></li><li><p>name the session after the directory</p></li><li><p>if a session already exists, attach to it</p></li></ul><p>If I run just <code>t</code> without any arguments, I&#8217;ll see a numbered list of recent sessions:</p><pre><code><code>IDX  SESSION               CREATED
1    codex                 2026-04-03 15:56:59
2    backend-worker        2026-04-03 15:22:10
3    docs                  2026-04-03 14:10:31</code></code></pre><p>And if I want to connect to any particular session from that list, I simply run</p><pre><code>t 2</code></pre><p>That made the process more convenient and also saved a lot of time when jumping between sessions.</p><h2><strong>OOM and cgroups</strong></h2><p>But there&#8217;s another problem with tmux - its server is the single point of failure.</p><p>On my dev box, I run many things in parallel.</p><p>At the same time, it could be:</p><ul><li><p>Compiling a project in Rust (like <a href="https://github.com/alexeygrigorev/codex-zcode/">Codex-ZCode bridge</a>)</p></li><li><p>Running Android emulator tests for <a href="https://pocketshell.io/">PocketShell</a></p></li><li><p>Running e2e tests with Playwright for <a href="https://aishippinglabs.com/">AI Shipping Labs</a></p></li></ul><p>If I&#8217;m unlucky and all these things run at the same time, my machine runs out of memory.</p><p>It&#8217;s usually not a problem for Android emulators or Playwright - they are simply killed when it happens. But if I run something like <code>cargo clippy</code> (a linter in Rust), it&#8217;s more dangerous.</p><p>OOM can not only kill the linter process, but also bring down the agent that&#8217;s running it, the shell that&#8217;s running the agent, the session that&#8217;s running the shell, and the tmux server too. When the tmux server dies, all the other sessions go with it.</p><p>So an OOM in Rust can wipe out all the tmux sessions on the machine. All of them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SknL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SknL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 424w, https://substackcdn.com/image/fetch/$s_!SknL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 848w, https://substackcdn.com/image/fetch/$s_!SknL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 1272w, https://substackcdn.com/image/fetch/$s_!SknL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SknL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png" width="1040" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:1040,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A cargo clippy workload exhausts memory. If the OOM also kills the shared tmux server, sessions A, B, and C all lose their PTYs&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A cargo clippy workload exhausts memory. If the OOM also kills the shared tmux server, sessions A, B, and C all lose their PTYs" title="A cargo clippy workload exhausts memory. If the OOM also kills the shared tmux server, sessions A, B, and C all lose their PTYs" srcset="https://substackcdn.com/image/fetch/$s_!SknL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 424w, https://substackcdn.com/image/fetch/$s_!SknL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 848w, https://substackcdn.com/image/fetch/$s_!SknL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 1272w, https://substackcdn.com/image/fetch/$s_!SknL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5179dca-882e-4edb-a262-cbe7075b461b_1040x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">If an OOM kills the tmux server, all three sessions lose their terminals.</figcaption></figure></div><p>An OOM kill doesn&#8217;t propagate from cargo to tmux. The kernel (&#8221;OOM killer&#8221;) decides who should receive <code>SIGKILL</code> when OOM happens. Usually it&#8217;s the process that caused the actual OOM. But <a href="https://github.com/tmux/tmux/issues/4151">sometimes</a> it may decide to kill the entire memory cgroup where it&#8217;s working, which includes the process, the shell, and the tmux server (bringing down all the sessions too).</p><p>A cgroup (control group) is a container around a group of processes that Linux manages like a single unit. It&#8217;s used to limit the CPU, memory and other resources each group can use. If a process in the group exceeds its memory limit, the kernel will target the processes inside that group for <code>SIGKILL</code>.</p><p>So a natural solution is to <a href="https://github.com/alexeygrigorev/rustkyll/blob/main/scripts/cargo-safe">run unsafe operations in an isolated cgroup</a>, so the OOM killer won&#8217;t touch anything outside.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kVz3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kVz3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 424w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 848w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 1272w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kVz3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png" width="920" height="610" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:610,&quot;width&quot;:920,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Agent B hits its cgroup memory limit and is killed. The tmux server, both shells, and agent A keep running.&quot;,&quot;title&quot;:&quot;Agent B hits its cgroup memory limit and is killed. The tmux server, both shells, and agent A keep running.&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Agent B hits its cgroup memory limit and is killed. The tmux server, both shells, and agent A keep running." title="Agent B hits its cgroup memory limit and is killed. The tmux server, both shells, and agent A keep running." srcset="https://substackcdn.com/image/fetch/$s_!kVz3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 424w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 848w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 1272w, https://substackcdn.com/image/fetch/$s_!kVz3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc563d87f-8e8d-4caa-943a-4cb3ea43f182_920x610.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Agent B is killed at its cgroup memory limit, while the tmux server and workload A keep running. Machine-wide OOM can still affect other groups.</figcaption></figure></div><p>However, you can&#8217;t know in advance which process will cause the OOM collapse. It kept happening to me over and over again. When I run 20-30 sessions in parallel, it&#8217;s very frustrating. </p><p>Eventually, I decided to run each tmux session in its own cgroup. Since I was already using tmuxctl as a wrapper around tmux, <a href="https://github.com/alexeygrigorev/tmuxctl/blob/6a20db9ac1b42b5c01fbce1e27fadcbda4fe0605/tmuxctl/robust.py#L326-L347">I added support for cgroups there</a>.</p><p>It worked well, but unfortunately it didn&#8217;t solve the main problem - the tmux server was still the single point of failure. Even with each session running in a cgroup, the server would die occasionally, bringing down all the sessions along with it.</p><p>After consulting Fable, we did the next logical thing: for each new tmux session start a separate server. So if one server dies, the rest keep running.</p><p>All that logic went into tmuxctl, which by that time became a Frankenstein monster, not just a simple wrapper around tmux CLI.</p><p>I got really tired of patching it up, and it felt like I was fighting tmux instead of using it. So I opened ChatGPT and asked &#8220;how difficult is it to write my own terminal multiplexer?&#8221;. It said it would be a few weeks of work. Then I turned on Pro mode and asked it to implement it. It thought for 10 minutes and gave me a first version written in Rust (which kind of worked).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VcKP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VcKP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 424w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 848w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 1272w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VcKP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png" width="1456" height="1099" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1099,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;My dictated ChatGPT prompt asking for a Linux terminal multiplexer in Rust that keeps other sessions alive when one runs out of memory&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="My dictated ChatGPT prompt asking for a Linux terminal multiplexer in Rust that keeps other sessions alive when one runs out of memory" title="My dictated ChatGPT prompt asking for a Linux terminal multiplexer in Rust that keeps other sessions alive when one runs out of memory" srcset="https://substackcdn.com/image/fetch/$s_!VcKP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 424w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 848w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 1272w, https://substackcdn.com/image/fetch/$s_!VcKP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c2f018c-ba00-40cb-918a-191b9106ef6c_2064x1558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">My brain dump, captured via dictation, that became the first version of aplexer.</figcaption></figure></div><p>I decided to call it &#8220;aplexer&#8221; which stands for &#8220;Agent Multiplexer&#8221;. &#8220;Amux&#8221; was already taken - I counted 3 products with this name, and none of them were doing what I needed. It&#8217;s very difficult to find a name that&#8217;s available on PyPI these days!</p><h2><strong>Aplexer&#8217;s initial requirements</strong></h2><p>What I needed from it:</p><ul><li><p>Simple commands to create sessions, list sessions, attach and detach</p></li><li><p>No server - no single point of failure.</p></li><li><p>Dealing with OOM errors without having to worry about cgroups</p></li></ul><p>Plus I wanted to have the same features that tmux had:</p><ul><li><p>Working after ssh disconnects</p></li><li><p>Attaching and detaching sessions</p></li><li><p>Sending input to each session</p></li><li><p>Seeing the history (scrolling up to see the previous messages from the agents)</p></li></ul><p>The main focus was on running agents, so I also wanted:</p><ul><li><p>Seeing which folder (&#8221;workspace&#8221;) each session is running in</p></li><li><p>Seeing what&#8217;s running inside each session - which agent</p></li><li><p>Letting agents send messages to each other natively</p></li></ul><p>I always follow the <a href="https://aishippingblog.com/p/ai-native-development-specifications">spec-driven development approach</a>, so I discussed the requirements with ChatGPT, got the <a href="https://github.com/PocketShell-io/aplexer/blob/main/spec.md">first specification</a> out, and started developing it.</p><h2><strong>Eat your own dog food</strong></h2><p>I don&#8217;t know why this approach is called that. But the idea is to start using the tool you develop as soon as possible.</p><p>This approach works extremely well and forces you to find and fix all the inconvenient points.</p><p>That&#8217;s why the focus for v0 of aplexer was to let me start using it instead of tmux, and then iterate and polish all the rough edges.</p><p>The main acceptance criteria for v0 were:</p><ul><li><p>I can use it for running agents</p></li><li><p>Agents run in detachable sessions that don&#8217;t stop after SSH disconnect</p></li><li><p>OOM in one session doesn&#8217;t affect any other session</p></li></ul><p>The first test that I implemented was causing OOM in one session and making sure the others weren&#8217;t affected. The <a href="https://github.com/PocketShell-io/aplexer/blob/main/tests/oom_isolation.rs">OOM isolation test</a> starts three sessions with 128 MB memory limits, exhausts the memory in session B, and checks that sessions A and C still respond to commands.</p><p>It took one week to have a stable version that works as v0, and then another couple of weeks to polish and add the features that I needed. (ChatGPT was right!)</p><p>I like short aliases, so I use <code>a</code> for aplexer in my terminal.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GhdB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GhdB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 424w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 848w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 1272w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GhdB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png" width="1456" height="1223" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1223,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Aplexer lists workspaces with agent sessions, including Claude, Codex, Grok, and OpenCode, with running and idle states&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Aplexer lists workspaces with agent sessions, including Claude, Codex, Grok, and OpenCode, with running and idle states" title="Aplexer lists workspaces with agent sessions, including Claude, Codex, Grok, and OpenCode, with running and idle states" srcset="https://substackcdn.com/image/fetch/$s_!GhdB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 424w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 848w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 1272w, https://substackcdn.com/image/fetch/$s_!GhdB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fdc834b-4a06-4446-b851-2d84c925df77_1684x1414.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">My sessions grouped by workspace, with the agent and its current state.</figcaption></figure></div><p>By now it has fully replaced tmux in my workflow, and I haven&#8217;t had a problem with OOM wiping out all my sessions since then.</p><h2><strong>Aplexer&#8217;s architecture</strong></h2><p>Each session in aplexer is independent of the others, and there&#8217;s no shared server. Instead, each session has a PTY worker that&#8217;s assigned only to that session.</p><p>When a new session is created, the aplexer client creates a worker, and the worker manages the PTY. Optionally, the process can be launched in a cgroup too.</p><p>Aplexer doesn&#8217;t need a central process for keeping track of all the sessions: it stores them in the filesystem.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ECnz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ECnz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 424w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 848w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ECnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png" width="1240" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1240,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The aplexer client routes to three independent session boundaries, each containing a worker and workload, with session B shown in red to illustrate an independent failure&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The aplexer client routes to three independent session boundaries, each containing a worker and workload, with session B shown in red to illustrate an independent failure" title="The aplexer client routes to three independent session boundaries, each containing a worker and workload, with session B shown in red to illustrate an independent failure" srcset="https://substackcdn.com/image/fetch/$s_!ECnz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 424w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 848w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ECnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe18b78e1-9ca9-4349-b978-a1c048f3f857_1240x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">aplexer gives each session its own worker, PTY, lifecycle, and optional workload cgroup.</figcaption></figure></div><p>I don&#8217;t need panes, windows and other things, so for me one session is one PTY.</p><p>But I still want to organize the sessions. I group sessions by workspace - the folder where the agents are running.</p><p>When I run <code>a</code>, I get a list of all the workspaces and a list of sessions in each.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n0ky!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n0ky!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 424w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 848w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 1272w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n0ky!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png" width="1456" height="651" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:651,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Workspace 7, dapier, contains six sessions including designer at index 2; community-base and ai-shipping-labs appear below it&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Workspace 7, dapier, contains six sessions including designer at index 2; community-base and ai-shipping-labs appear below it" title="Workspace 7, dapier, contains six sessions including designer at index 2; community-base and ai-shipping-labs appear below it" srcset="https://substackcdn.com/image/fetch/$s_!n0ky!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 424w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 848w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 1272w, https://substackcdn.com/image/fetch/$s_!n0ky!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb960c1c-bc0d-496b-abf5-b8b531ef4b82_1710x764.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A workspace can contain several sessions, each with its own tag and agent.</figcaption></figure></div><p>Now if I want to attach to any of the sessions, I can type</p><pre><code>a 7 2</code></pre><p>This will attach to the workspace number 7 (<code>~/git/dapier</code>), session 2 (<code>designer</code>). I can refer to them by names too, but that&#8217;s too much to type.</p><h2><strong>Making it agent-aware</strong></h2><p>I also wanted aplexer to keep track of what&#8217;s running inside each session. It shows which agent is there, whether it is running or idle, and when it was last active.</p><p>If I want to start a Codex session with the tag &#8220;code-refactor&#8221; in the current directory, I simply type:</p><pre><code>a - codex code-refactor</code></pre><p>Of course, that&#8217;s all documented in the <a href="https://github.com/PocketShell-io/aplexer/">README</a>.</p><h2><strong>Communication</strong></h2><p>In tmux my agents were already talking to each other, so I wanted to have the same functionality in aplexer too.</p><p>This is what it looks like:</p><pre><code>a send code-refactor "Please review the current diff and report any bugs." --enter</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3uir!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3uir!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3uir!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3uir!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3uir!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3uir!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg" width="1280" height="880" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:880,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Codex receives a cross-workspace coordination request through aplexer&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Codex receives a cross-workspace coordination request through aplexer" title="Codex receives a cross-workspace coordination request through aplexer" srcset="https://substackcdn.com/image/fetch/$s_!3uir!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3uir!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3uir!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3uir!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff474c197-7d62-44c5-95c2-4d6aa59f8f95_1280x880.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The agents coordinate a review and integration across workspaces</figcaption></figure></div><p>And check what&#8217;s on the screen:</p><pre><code>a capture code-refactor --screen</code></pre><p>These commands send text as the prompt. But there&#8217;s also a message bus that the agents can use for asynchronous communication.</p><p>Within a session it looks like this:</p><pre><code>a message send --to code-refactor "Implementation is ready. Please review the diff."
a message send --all "The tests pass. I&#8217;m ready for review."</code></pre><p>The recipient can read its inbox, acknowledge it, and then reply when it&#8217;s ready:</p><pre><code>a message inbox
a message ack &lt;message-id&gt;
a message reply &lt;message-id&gt; &lt;message&gt;</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Ky6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Ky6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Ky6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg" width="1080" height="386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:386,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Agent sends an integration push notice to its peer using aplexer message reply&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Agent sends an integration push notice to its peer using aplexer message reply" title="Agent sends an integration push notice to its peer using aplexer message reply" srcset="https://substackcdn.com/image/fetch/$s_!1Ky6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1Ky6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa688b766-24b7-43a3-8234-737c98a07376_1080x386.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The agent replies after coordinating an isolated worktree integration</figcaption></figure></div><h2><strong>Aplexer and PocketShell</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x_Gi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x_Gi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 424w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 848w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 1272w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x_Gi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png" width="1456" height="978" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:978,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:205112,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/218398320?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!x_Gi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 424w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 848w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 1272w, https://substackcdn.com/image/fetch/$s_!x_Gi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e0ecf5-010f-4928-b6aa-13c2a9ee7750_1878x1262.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>tmux is excellent at what it does, but it stopped working for me for running agents on my dev box. So I created aplexer. Aplexer is an agent multiplexer written in Rust that doesn&#8217;t require a single shared server. It groups my sessions in workspaces, and I can tag them and see their state.</p><p>If you run agents on a Linux dev box and want to try it, the <a href="https://github.com/PocketShell-io/aplexer/blob/main/README.md">README</a> has installation instructions and the full command reference.</p><p>Now I rarely use aplexer directly - I mostly use it via <a href="https://pocketshell.io/">PocketShell</a> to manage my sessions. It&#8217;s an app that lets me connect to my dev box from my Android phone, from my laptop or from the web. It lists all the sessions there and lets me switch easily between them.</p><p>I started working on it in May and I like how it&#8217;s coming along. I&#8217;ll polish it a bit more and soon will write another blog post on how I use it for my work with agents, so you can try it too.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mMXv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mMXv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 424w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 848w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mMXv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png" width="1456" height="918" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3242a0ca-c318-4432-a497-000693a66319_2596x1636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:918,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;PocketShell&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="PocketShell" title="PocketShell" srcset="https://substackcdn.com/image/fetch/$s_!mMXv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 424w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 848w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!mMXv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3242a0ca-c318-4432-a497-000693a66319_2596x1636.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Managing agent sessions with PocketShell. Each agent runs in aplexer, and the list of sessions on the side bar is coming from aplexer too.</figcaption></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Last Call: AI Engineering Buildcamp Cohort 4 ]]></title><description><![CDATA[The enrollment deadline is next week on Monday]]></description><link>https://aishippingblog.com/p/last-call-ai-engineering-buildcamp</link><guid isPermaLink="false">https://aishippingblog.com/p/last-call-ai-engineering-buildcamp</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Wed, 23 Sep 2026 11:26:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bRe3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We started the new cohort of AI Engineering Buildcamp this Monday, but you can still join. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bRe3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bRe3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bRe3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg" width="1280" height="777" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:777,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:152315,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/217046991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bRe3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bRe3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ebf638a-b71b-4ae4-bd40-34a68d083e27_1280x777.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We had our first live session, where I went through the course structure, talked about projects, and answered questions from the participants.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>In this post, I want to focus on what we discussed during the launch session.</p><p>In particular, I&#8217;ll cover:</p><ul><li><p>Course curriculum</p></li><li><p>How the capstone project works</p></li><li><p>How to come up with a project idea</p></li><li><p>How I&#8217;ll help with the project scope</p></li><li><p>How we&#8217;ll use coding agents</p></li><li><p>Common questions</p></li></ul><p>Registration is still open until next Monday, September 28, and there&#8217;s one buffer week to catch up if you&#8217;re joining late.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://maven.com/alexey-grigorev/from-rag-to-agents&quot;,&quot;text&quot;:&quot;Join AI Engineering Buildcamp&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://maven.com/alexey-grigorev/from-rag-to-agents"><span>Join AI Engineering Buildcamp</span></a></p><h2>Course Overview</h2><p>I already wrote about the course in detail in my previous post. I covered the curriculum, homework projects, projects from the previous cohorts, workload, and other details in that post.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;1ed284d7-bf95-4b00-8ea4-aea1ce212666&quot;,&quot;caption&quot;:&quot;Next Monday (September 21), I start the next cohort AI Engineering Buildcamp. It&#8217;s going to be the forth cohort of the course.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;AI Engineering Buildcamp Starts Next Week&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-09-14T17:21:42.752Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!CsUw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/ai-engineering-buildcamp-starts-next&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:215695735,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:14,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Let me do a quick recap: </p><ul><li><p>Week 1 is about the foundations. We prepare the environment, learn how to work with LLMs, and then focus on RAG and search.</p></li><li><p>Week 2 is a buffer week. I want to make sure everyone has enough time to prepare the environment. If you&#8217;re joining a bit late, you will use this time to catch up. </p></li><li><p>Week 3: agents. We go from regular RAG to agentic RAG, learn about tool calling, and implement the agent loop ourselves, and then use an agentic framework.</p></li><li><p>Week 4: testing. We add tests to our agent and make sure it behaves as expected.</p></li><li><p>Week 5: monitoring. We log all the interactions between the users and the agent.</p></li><li><p>Week 6: evaluation. We create evaluation datasets, use LLM judges, and systematically evaluate the system.</p></li><li><p>Weeks 7 and 8: focused project work.</p></li><li><p>Week 9: peer review and demo day.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TqWv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TqWv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 424w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 848w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 1272w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TqWv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png" width="1672" height="721" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:721,&quot;width&quot;:1672,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1211922,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/217046991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c4297ed-e8b8-46eb-80ee-4dce21efc318_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TqWv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 424w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 848w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 1272w, https://substackcdn.com/image/fetch/$s_!TqWv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc45ab45e-b901-4d49-98cf-b7c4a2448954_1672x721.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The course takes 9 weeks: 6 week of core work and 3 weeks of project work.</p><h2>The Capstone Project</h2><p>The project is the most important part of the course. I want everyone to finish with a project that you can add to your project portfolio, use at work or show it to potential clients. </p><p>For the capstone, you need to come up with a project idea and implement it end-to-end. It&#8217;ll be a RAG application, an agent, or something in between. You will cover it with tests, add monitoring, evaluate it and deploy to the cloud. </p><p>In our curriculum, weeks 7 and 8 are specifically dedicated to project work, but I want you to start working on your projects from week 1. This way, by the end of week 6 you have something working, and you can spend weeks 7 and 8 polishing it and finishing the missing parts.</p><p>I you already have an idea - that&#8217;s great, I&#8217;ll help you refine it.</p><p>But if you don&#8217;t, I&#8217;ll give you a project ideation process that you can follow to select a meaningful idea to work on. This approach uses design thinking to discover problems you can solve with AI.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LLyp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LLyp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 424w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 848w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 1272w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LLyp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png" width="1456" height="786" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/deb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:786,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1423820,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/217046991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LLyp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 424w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 848w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 1272w, https://substackcdn.com/image/fetch/$s_!LLyp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdeb5b3b8-a6e1-425e-b62f-53d4b6c819eb_1707x921.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Coding Agents</h2><p>I also want to encourage you to use coding agents during the course.</p><p>During the lessons, we write code together. When I cover something in the coding sessions, I want you to understand how things work. For example we first implement the tool-calling loop ourselves before using any agent framework.</p><p>But once you understand the core concepts and the code from the lessons, you can give it to a coding agent:</p><blockquote><p>Based on the code we created in the lesson, I want to create &lt;YOUR PROJECT IDEA&gt;. Please use this implementation as a reference and adapt it to my project.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8aOo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8aOo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 424w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 848w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 1272w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8aOo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png" width="1952" height="1243" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1243,&quot;width&quot;:1952,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:244253,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/217046991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a84025e-5c83-480e-8f07-9d3dc8618403_1952x1318.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8aOo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 424w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 848w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 1272w, https://substackcdn.com/image/fetch/$s_!8aOo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe46add96-5d0b-4421-a38a-4ce62d66ee68_1952x1243.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It makes the project development much faster. Instead of starting from zero, the coding agent already has an example that it will use for your own project. You already understand the original implementation, so understanding the code for your project will be much easier. </p><p>This is how I recommend using coding agents: not as a replacement for learning, but as a tool that can get us from an idea to a working implementation faster.</p><p></p><p>See what people from Cohort 3 implemented:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;bc9cdb3c-7601-40cc-ae87-800f1461e48b&quot;,&quot;caption&quot;:&quot;The third cohort of my AI Engineering course wrapped this summer, and in this post I want to share what we built.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;13 AI Projects from AI Engineering Buildcamp Cohort 3&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-09-04T18:21:30.585Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04db2d9f-7355-49f4-bce5-58a862c6364e_1456x1048.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/13-ai-projects-from-ai-engineering&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:214196526,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:15,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><h2>Questions From the Launch Session</h2><p>We had a few questions during the launch stream and I think they are quite interesting, so I wanted to cover them here too.</p><h3>Is RAG Dead?</h3><p>No, it&#8217;s not dead. On the contrary, according to my analysis for <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineering Field Guide</a>, the majority of jobs require it. That&#8217;s why we start the course from RAG and cover it in detail.</p><p>The reasons we need it:</p><ul><li><p>The model doesn&#8217;t have access to proprietary data. If your company has an internal knowledge base, the model has never seen it. With RAG, you can search this knowledge base, retrieve the relevant information, and include it in the context.</p></li><li><p>These knowledge bases are usually not that large. With models capable of processing 128 million tokens and more, we can fit the entire dataset into the context window. The RAG is still needed though: if you send the entire dataset to the model, it will have hard time figuring out what&#8217;s useful. But if you retrieve only the relevant pieces, you send less data. This makes it faster and cheaper, and it also gives the model more focused context.</p><p></p></li></ul><h3>Are We Going to Cover Fine-Tuning?</h3><p>No. Fine-tuning is slow, expensive, and often not needed.</p><p>If we have our internal documentation, we can prepare a training dataset in a special format, take a model with open weights, rent a GPU, and spend a day or two fine-tuning it, so it actually learns from our data. Next time we use it, we don&#8217;t need to inject the context with RAG: the model already has our knowledge base internally in its weights. </p><p>There are a few problems with fine-tuning though: </p><ul><li><p>You need to have a lot of data and prepare it in a special way</p></li><li><p>GPUs aren&#8217;t cheap and not always available</p></li><li><p>It requires time to tune the model </p></li><li><p>It&#8217;s difficult to enforce answering with the learned information instead of the general knowledge it had </p></li><li><p>The worst part: you update the dataset by including one new page and you need to fine-tune again</p></li></ul><p>Now compare it with RAG: we just update the documents in the search index and that&#8217;s it. These problems make fine-tuning impractical for the majority of use-cases. And my job analysis shows that most companies don&#8217;t actually require it.</p><p>That&#8217;s why we don&#8217;t cover it in the course. </p><h3>Do We Need Multiple Agents?</h3><p>In most cases you don&#8217;t. In many cases you don&#8217;t even need an agent, just RAG is enough.</p><p>I will help you understand when you need what, and scope your project in a way that it&#8217;s focused, manageable - and also possible to implement during the course.</p><p>We do have a few places where I cover multi-agent setup. For example, the Deep Research Agent uses multiple agents for different parts of the research process, or the Coding Agent that uses the plan-then-implement pattern. </p><p>But it&#8217;s not the focus of the course. It&#8217;s more important to first understand how to create one agent well, test it, add monitoring and evaluate it to make sure it works properly. And then, only if the problem actually requires more complexity, add it. (In many cases it doesn&#8217;t.)</p><h2>Can I Use Another Technology Stack?</h2><p>In the course we use OpenAI, Python, Pydantic AI and other technologies to implement agents.</p><p>But you don&#8217;t have to use these specific technologies in your capstone. If you want to build your project in TypeScript - that&#8217;s fine. You want to learn LangChain and use it instead of Pydantic AI - totally fine too. Anthropic, Gemini, or another provider work well instead of Open AI.</p><p>If you don&#8217;t have a strong reason to choose something else, I recommend using the same stack as in the course. It will be easier for me to help you.</p><p>But it&#8217;s not a requirement.</p><h2>Do I Need an Expensive Coding Agent Subscription?</h2><p>No. In fact, even a free subscription will be sufficient. There are a few places in the course where I use a coding agent to generate code - for example, for creating a streamlit tool for evals. But for that I use free Antigravity, so everyone can reproduce it. Some students prefer not to use any coding agent at all and it works too. </p><p>I&#8217;d still recommend investing at least $10 in getting an OpenCode subscription or $20 for Claude Code Pro or ChatGPT Plus, so you don&#8217;t need to worry about hitting the usage limits with the free subscriptions.</p><p>Also check my recent post about free and affordable coding agents:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;86e0abeb-4c28-467b-a141-b8a9ee468f24&quot;,&quot;caption&quot;:&quot;I recently posted on X that we have access to so many tokens that I&#8217;m running out of ideas what to build.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Free and Affordable AI-Native Development&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-09-11T23:02:46.666Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!zfRh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/free-and-affordable-ai-native-development&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:215293864,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:16,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><h2>What If I Can&#8217;t Attend the Live Sessions?</h2><p>The course is designed to be asynchronous. </p><p>Each week we have a live office hour session. On these sessions we discuss questions, project ideas, architecture decisions, and other things that come up during the week. </p><p>If you can&#8217;t attend, it&#8217;s recorded and shared shortly after. You can also ask your questions in advance and we will cover them during the session. </p><p>Most of the content is already recorded as videos (with text articles along them), which you can watch whenever it&#8217;s convenient for you.</p><h2>Can It Help me Transition into AI Engineering? </h2><p>Yes.</p><p>In previous cohorts we had participants from different backgrounds: software engineers, DevOps engineers, data scientists, researchers, and others. For many people the course helped them start doing AI engineering work in their existing company. Others used what they learned when looking for a new role. Some use these skills for freelancing. </p><p>If finding an AI engineering job after this course is one of your goals, I recommend thinking about the capstone as a portfolio project from the beginning. Build something you can include in your CV.</p><p>If you need help with that, I&#8217;ll be very happy to support.</p><p></p><p>I always enjoy our launch streams and this one was not an exception. It&#8217;s going to be a lot of fun! </p><h1>Join Cohort 4</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!maJv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!maJv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 424w, https://substackcdn.com/image/fetch/$s_!maJv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 848w, https://substackcdn.com/image/fetch/$s_!maJv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!maJv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!maJv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png" width="1456" height="590" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:590,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:859501,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/217046991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!maJv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 424w, https://substackcdn.com/image/fetch/$s_!maJv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 848w, https://substackcdn.com/image/fetch/$s_!maJv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!maJv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31eb9d6-bd76-4aaa-9d29-ab844b168f42_2762x1120.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Registration closes next Monday, September 28. Use code &#8220;SUBSTACK&#8221; to get 20% off. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://maven.com/alexey-grigorev/from-rag-to-agents&quot;,&quot;text&quot;:&quot;Join Cohort 4&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://maven.com/alexey-grigorev/from-rag-to-agents"><span>Join Cohort 4</span></a></p><p>See you on Monday! </p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Jev: A Decision Model That Does Not Generate Text]]></title><description><![CDATA[If you&#8217;ve been on X in the last few days, chances are that you&#8217;ve seen posts about Jev from TypeSave AI.]]></description><link>https://aishippingblog.com/p/jev-a-decision-model-that-does-not</link><guid isPermaLink="false">https://aishippingblog.com/p/jev-a-decision-model-that-does-not</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 18 Sep 2026 12:36:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!U0Eg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you&#8217;ve been on X in the last few days, chances are that you&#8217;ve seen posts about Jev from TypeSave AI. Every time I open my timeline, I see people posting about it.</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/CompleteSkeptic/status/2099925682726002904&quot;,&quot;full_text&quot;:&quot;After co-inventing ChatGPT, I kept asking myself: why have superhuman chat models not led to AGI?\n\nI&#8217;ve spent the last 2 years in stealth building a new way to train models (RLCD), and a new type of frontier AI model that we are releasing today: Jev\n\n&#8226; 20-200x faster\n&#8226; 40-400x &#8230;&quot;,&quot;username&quot;:&quot;CompleteSkeptic&quot;,&quot;name&quot;:&quot;Diogo Almeida&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/1650708125685800960/7k6r0UZg_normal.jpg&quot;,&quot;date&quot;:&quot;2026-09-15T18:17:52.000Z&quot;,&quot;photos&quot;:[{&quot;img_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!z-75!,w_1028,c_limit,f_auto,q_auto:best,fl_progressive:steep/l_play_button_usfui2,w_88,e_colorize:0/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F__ss-rehost__tw-video-preview-13_2099925575637057536.jpg&quot;,&quot;link_url&quot;:&quot;https://t.co/JSybNG2BKJ&quot;}],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:3509,&quot;retweet_count&quot;:7023,&quot;like_count&quot;:66618,&quot;impression_count&quot;:31687933,&quot;expanded_url&quot;:null,&quot;video_url&quot;:&quot;https://video.twimg.com/amplify_video/2099925575637057536/vid/avc1/1280x720/ZoKJ_BS5SNaqG45k.mp4&quot;,&quot;video_preview_media_key&quot;:&quot;13_2099925575637057536&quot;,&quot;belowTheFold&quot;:false}" data-component-name="Twitter2ToDOM"></div><p>The model <a href="https://openrouter.ai/typesafe/jev-1.13">was recently released on OpenRouter</a>, so I decided to give it a try.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Jev and TypeSafe AI</strong></h2><p>I saw the announcement of Jev on X a few days. It&#8217;s interesting because it doesn&#8217;t generate text. When I first read about it, I thought, &#8220;Huh? No text?&#8221; After seeing a few more posts, I realized that it could be useful for quick decision-making.</p><p>Jev is a very fast one-shot text classifier. You give it text, and it makes a decision. You don&#8217;t need to train or fine-tune it. </p><p>That makes it useful as a router:</p><ul><li><p>Should I send this request to a heavy model or a light one?</p></li><li><p>Which agent should I select?</p></li><li><p>Should I handle this request automatically or send it to a human?</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6XLF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6XLF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 424w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 848w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 1272w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6XLF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png" width="1456" height="847" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:847,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:353079,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/216290283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6XLF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 424w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 848w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 1272w, https://substackcdn.com/image/fetch/$s_!6XLF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e455134-49ce-47e5-a98d-11f7fb643661_2428x1412.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev">TypeSafe calls Jev a &#8220;System One Model&#8221;</a>. The name comes from &#8220;Thinking, Fast and Slow&#8221;. Yes, that&#8217;s this thick, long and boring book that I tried to read but gave up (I managed to finish the audio version though).</p><ul><li><p>System 1 is the fast, intuitive judgment your brain makes without effort.</p></li><li><p>System 2 is slow, deliberate reasoning.</p></li></ul><p>So, Jev is System 1, and LLMs are System 2. </p><p>Jev isn&#8217;t a replacement for LLMs. It&#8217;s a layer that sits in front of them.</p><p>You can also use it anywhere you previously relied on a cheap, fast LLM with structured output to make a decision.</p><p>For example, it can act as a guardrail. In <a href="https://aishippinglabs.com/workshops/agent-with-guardrails">my AI Shipping Labs workshop about guardrails</a>, I used <code>gpt-4o-mini</code> as a quick classifier to decide whether to continue generation. Jev can replace that binary LLM classifier.</p><h2><strong>The API</strong></h2><p>You send Jev one request containing two things:</p><ul><li><p>a state (unstructured text or a JSON object describing the situation)</p></li><li><p>a set of typed questions</p></li></ul><p>Jev answers all questions in parallel and returns structured JSON with probability distributions.</p><p>There are three question types (<a href="https://docs.typesafe.ai/primitives">TypeSafe calls them &#8220;primitives&#8221;</a>):</p><ul><li><p>Noul: a yes/no question. Returns a single probability from 0 to 1. &#8220;Does this message request a refund?&#8221; might return <code>0.93</code>. Think binary classifier.</p></li><li><p>Choice: pick one option from a set of options. Returns the selected option, a probability distribution across all options, and a confidence score. Up to 255 options per question. Think multiclass classification.</p></li><li><p>Score: rate the state on an ordered scale. Returns a weighted score, the distribution across levels, and confidence. Between 2 and 10 levels. Think ordinal classification.</p></li></ul><p>Here&#8217;s a request that includes all three question types for one state:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;8c73393b-d41a-4317-b39e-c81ea174b2b4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "is_urgent": {
      "type": "noul",
      "instructions": "Does this convey urgency?"
    },
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this?",
      "criteria": {
        "billing": "Payments, invoicing, refunds",
        "technical": "Bugs, outages, integrations",
        "sales": "Pricing, upgrades, new accounts"
      }
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": ["Calm", "Frustrated", "Very angry"]
    }
  }
}</code></pre></div><p>The <code>state</code> is the text that we analyze (it could also be a JSON object). Each entry in <code>questions</code> is one question with whatever key we choose. Inside, <code>instructions</code> is the question that the model needs to answer, and <code>criteria</code> describes the possible answers:</p><ul><li><p>a map of options for Choice</p></li><li><p>an ordered list of levels for Score</p></li><li><p>an optional explanation of yes and no for Noul.</p></li></ul><p>Here&#8217;s what a response looks like:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;592b13c5-75df-4d42-b403-2b38ebf5c556&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
  "model": "jev-1.13.0",
  "answers": {
    "is_urgent": { "type": "noul", "noul": 0.93 },
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.84, "technical": 0.15, "sales": 0.0, "other": 0.01 },
      "confidence": 0.6
    },
    "frustration": {
      "type": "score",
      "score": 1.3,
      "probabilities": { "0": 0.0, "1": 0.7, "2": 0.3 },
      "confidence": 0.54
    }
  }
}</code></pre></div><p>The response contains one typed answer for each question. The <code>noul</code> value is the probability that the message is urgent, <code>choice</code> is the selected department, and <code>score</code> is the weighted frustration level.</p><p>We can now use these answers in our code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;253a1faa-efb6-45a0-84f9-66b806874379&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">urgency = answers["is_urgent"]["noul"]
department = answers["department"]["choice"]
department_confidence = answers["department"]["confidence"]

if urgency &gt; 0.8 and department == "billing":
    escalate_to_billing()
elif department_confidence &lt; 0.5:
    route_to_human()</code></pre></div><h2><strong>Benchmarks</strong></h2><p><a href="https://evals.typesafe.ai/">TypeSafe built a benchmark suite</a> of four business workflows to show how it works:</p><ul><li><p>security incident triage</p></li><li><p>agent trace observability</p></li><li><p>invoice processing</p></li><li><p>customer service</p></li></ul><p>Each model ran through the same workflow harness, and the results were compared against a reference policy derived from the average judgments of GPT-6 Astra and Claude Fable 5.1.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T64v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T64v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 424w, https://substackcdn.com/image/fetch/$s_!T64v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 848w, https://substackcdn.com/image/fetch/$s_!T64v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 1272w, https://substackcdn.com/image/fetch/$s_!T64v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T64v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png" width="1456" height="412" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/43717639-c157-47d8-b383-de02faf87860_2084x590.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:412,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:126915,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/216290283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T64v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 424w, https://substackcdn.com/image/fetch/$s_!T64v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 848w, https://substackcdn.com/image/fetch/$s_!T64v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 1272w, https://substackcdn.com/image/fetch/$s_!T64v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43717639-c157-47d8-b383-de02faf87860_2084x590.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A separate <a href="https://github.com/gemanor/jev-code-review-benchmark">code-review benchmark by an independent developer</a> compared Jev against Gemini Flash and Claude Fable on Python linting rules.</p><ul><li><p>Jev cost 45 times less than Gemini Flash and 274 times less than Claude Fable.</p></li><li><p>Its median response time was 0.75 seconds (versus 3.59 seconds for Flash and 4.31 seconds for Fable).</p></li><li><p>Jev scored 98% correctness versus 100% for both comparators.</p></li></ul><h2><strong>Applications</strong></h2><p>I&#8217;ve seen a lot of posts on X about what people have built with Jev over the last few days.</p><h3><strong>Self-Driving Simulations</strong></h3><ul><li><p>A 2D top-down browser simulation where Jev acts as the driving classifier, receiving road state and making steering decisions. <a href="https://github.com/vinilana/live-jev">Source</a></p></li><li><p>A 3D Three.js driving simulator. Jev receives a compact JSON snapshot of current speed, speed limit, next turn distance, nearby vehicles and pedestrians with relative positions and speeds, traffic signals, and predicted hazards. It returns a driving decision. <a href="https://github.com/standardagents/jevdrive">Source</a></p></li><li><p>An interactive playground with 3D driving simulations showing real AI decisions with visible sensor inputs. <a href="https://github.com/kavehmz/typesafe-playground">Source</a></p></li></ul><h3><strong>Games</strong></h3><p>TypeSafe&#8217;s launch post shows Jev playing Doom at about 10 queries per second, at a cost of roughly $7 per hour.</p><p>People also made it play chess:</p><ul><li><p>Jev versus an OpenAI model. <a href="https://github.com/wondertwins/jev-benchmark">Source</a></p></li><li><p>Two Jev instances playing against each other. <a href="https://github.com/TholeG/typesafe-chess">Source</a></p></li></ul><p>Other games:</p><ul><li><p>An NPC demo tested whether Jev could decide which characters should respond when a player speaks. The game engine sends the transcript and NPC descriptions, and Jev decides which NPC the player is addressing. <a href="https://github.com/wondertwins/jev-benchmark">Source</a></p></li><li><p>Wikiracing, where Jev navigates from one Wikipedia page to another using only links on each page. Jev had to choose among hundreds or thousands of links per page. <a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev">Source</a></p></li></ul><h2><strong>Using Jev as an Agent Router for My Work</strong></h2><p>I wanted to give it a try too. Since Jev became available via OpenRouter today, I could skip the waiting list.</p><p>Currently, I&#8217;m automating my work at <a href="https://datatalks.club/">DataTalks.Club</a>, <a href="https://aishippinglabs.com/">AI Shipping Labs</a> and I also have a lot of other operational work.</p><p>Because each piece of work is different and requires different tools, I need a different agent for each task:</p><ul><li><p>If a task is about DataTalks.Club course operations (cohorts, homeworks, projects, leaderboards, certificates) or DataTalks.Club events (workshops or podcast interviews), it should go to the DTC agent.</p></li><li><p>If a task is about AI Shipping Labs (workshops, events, membership and payments, sprints, marketing pages), it should go to the AISL agent.</p></li><li><p>Otherwise, it should go to the admin agent.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U0Eg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U0Eg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 424w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 848w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 1272w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U0Eg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png" width="1020" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:1020,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Jev routes one request to one of three specialist agents&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Jev routes one request to one of three specialist agents" title="Jev routes one request to one of three specialist agents" srcset="https://substackcdn.com/image/fetch/$s_!U0Eg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 424w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 848w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 1272w, https://substackcdn.com/image/fetch/$s_!U0Eg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F416fad25-b9ac-40c8-a9ba-1340a6f162db_1020x440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">I use Jev to route the task to the appropriate specialist agent</figcaption></figure></div><p></p><h3><strong>Routing with structured output</strong></h3><p>Previously, I&#8217;d use a simple model like <code>gpt-4o-mini</code> for routing with a structured output schema:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2c694d4a-ad0c-4293-a6d8-7546ca037f3d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from enum import Enum
from pydantic import BaseModel, Field

class Destination(str, Enum):
    DATATALKS_CLUB = "datatalks_club"
    AI_SHIPPING_LABS = "ai_shipping_labs"
    ADMIN = "admin"

class RouteDecision(BaseModel):
    destination: Destination = Field(
        description=(
            "Which specialist should handle this request? Choose datatalks_club for "
            "DataTalks.Club course operations: cohorts, homeworks, learner projects "
            "and peer review, leaderboards, certificates, registrations, and meetups. "
            "Choose ai_shipping_labs for AI Shipping Labs operations: workshops and "
            "the membership tiers that gate them (main+ and above), events, plans and "
            "payments, shipping sprints, the book club, marketing pages, campaigns, "
            "and member CRM notes. Choose admin for personal task tracking in a local "
            "task list: capturing a task or reminder, listing tasks, marking one done."
        )
    )</code></pre></div><p>The enum describes the directions to route the agent, so I first run a classifier to decide which agent to invoke, and then invoke that agent.</p><p>In code it can look like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;d4cafd4a-15a9-4cee-8722-a811a4149b7b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic_ai import Agent
from site_agents.llm import openrouter_model

router = Agent(
    openrouter_model("openai/gpt-4o-mini"),
    instructions=GPT_ROUTER_INSTRUCTIONS,
    output_type=RouteDecision,
)

result = await router.run(request)
routing_decision = result.output

agents[routing_decision].run(request)</code></pre></div><h3><strong>Routing with Jev</strong></h3><p>With Jev, the same routing decision is one <code>Choice</code> question:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;da70ef21-d856-4571-8e95-e3207a1c4798&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">ROUTER_INSTRUCTIONS = """
You are the dispatch layer for a small operations assistant.
Classify the user&#8217;s request into the single specialist that can do the work.
Do not answer the request and do not invent a fourth category.
""".strip()

ROUTING_CRITERIA = {
    "datatalks_club": (
        "DataTalks.Club course operations: cohorts and courses, homeworks and their "
        "questions, learner projects and peer review, leaderboards, certificates, "
        "graduates, course registrations, and community meetups."
    ),
    "ai_shipping_labs": (
        "AI Shipping Labs operations: workshops and the membership tiers that gate "
        "them (main+ and above), events, plans, payments and tier overrides, shipping "
        "sprints and their weekly checkpoints, the book club, marketing pages and "
        "site navigation, email campaigns, and member CRM notes."
    ),
    "admin": (
        "Personal task tracking in a local task list: capturing a task or reminder, "
        "listing open or completed tasks, and marking a task done. No website, course, "
        "workshop, or membership system is involved."
    ),
}</code></pre></div><p>Now we prepare the payload and send a request:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;50ba89b6-5c9e-414c-b641-29642e4e7507&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">payload = {
    "model": "~typesafe/jev-latest",
    "state": request,
    "questions": {
        "destination": {
            "type": "choice",
            "instructions": (
                f"{ROUTER_INSTRUCTIONS}\n\n"
                "Which specialist should handle this request? "
                "Choose exactly one option."
            ),
            "criteria": ROUTING_CRITERIA,
        }
    },
}

response = await client.post(
    "/api/alpha/decisions",
    headers={"Authorization": f"Bearer {api_key}"},
    json=payload,
)</code></pre></div><p>The answer comes back in <code>destination</code>. Since the model can only return one of my three options, I can map it to <code>Literal</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;06446533-755d-4744-909b-6970fa1e9528&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from typing import Literal

class JevChoiceAnswer(BaseModel):
    type: Literal["choice"]
    choice: Destination
    probabilities: dict[str, float]
    confidence: float</code></pre></div><p>From there, dispatching is the same:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;f9236e6b-10bd-4b45-8eaf-19e9b7777cb5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">route = await route_request(request)
routing_decision = result.destination
agents[routing_decision].run(request)</code></pre></div><h3><strong>Comparing the Two Routers</strong></h3><p>To compare Jev with <code>gpt-4o-mini</code>, I created 50 short, hand-labeled requests and marked the destination for each one. They cover the work each site actually has: cohorts, homeworks, projects and leaderboards for DataTalks.Club, workshops, membership tiers, sprints and marketing pages for AI Shipping Labs, and plain task capture for the admin list.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;50a5226d-8fa8-4d63-be94-da141156ab98&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">[
  {
    "id": "aisl-01",
    "request": "Create a workshop Tuesday 16:30 about JEV, main+ only.",
    "expected": "ai_shipping_labs"
  },
  {
    "id": "admin-02",
    "request": "Remind me tomorrow to submit the VAT receipts.",
    "expected": "admin"
  }
]</code></pre></div><p>These are the results:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!N9zR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N9zR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 424w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 848w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 1272w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!N9zR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png" width="1456" height="318" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:318,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Benchmark results: Jev 50/50 at 0.32 s median, 0.55 s p95 and $0.001089, gpt-4o-mini 50/50 at 0.76 s median, 1.17 s p95 and $0.003704&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Benchmark results: Jev 50/50 at 0.32 s median, 0.55 s p95 and $0.001089, gpt-4o-mini 50/50 at 0.76 s median, 1.17 s p95 and $0.003704" title="Benchmark results: Jev 50/50 at 0.32 s median, 0.55 s p95 and $0.001089, gpt-4o-mini 50/50 at 0.76 s median, 1.17 s p95 and $0.003704" srcset="https://substackcdn.com/image/fetch/$s_!N9zR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 424w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 848w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 1272w, https://substackcdn.com/image/fetch/$s_!N9zR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee39d0a-b3ad-486c-9bf7-fbaf3f3e1014_1594x348.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Both routers get every case right. It&#8217;s not a suprsize, though. But speed and price are different: Jev was 2.4 times faster and 3.4 times cheaper.</p><p>I ran the benchmark several times and the numbers barely moved - Jev&#8217;s median stayed between 0.32 and 0.34 seconds, gpt-4o-mini&#8217;s between 0.73 and 0.76 second. The cost was the same on every run.</p><p>I think it&#8217;s a very promising technology and I&#8217;ll most likely integrate it in my workflows.</p><h3><strong>Workshop on Tuesday</strong></h3><p>I want to do a more detailed workshop about Jev next Tuesday at 16:30 Berlin time on AI Shipping Labs. If you&#8217;re member, come join me!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/events/56/building-with-jev&quot;,&quot;text&quot;:&quot;Sign up for workshop&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/events/56/building-with-jev"><span>Sign up for workshop</span></a></p><p>If you have a use case, bring it along too. See you there!</p><h2><strong>Tools</strong></h2><p>This week I bookmarked a few interesting tools that I want to try.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EmdW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EmdW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 424w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 848w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 1272w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EmdW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png" width="1456" height="1050" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1050,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rebuno dashboard showing an agent execution with durable steps, approvals, and execution statuses&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rebuno dashboard showing an agent execution with durable steps, approvals, and execution statuses" title="Rebuno dashboard showing an agent execution with durable steps, approvals, and execution statuses" srcset="https://substackcdn.com/image/fetch/$s_!EmdW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 424w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 848w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 1272w, https://substackcdn.com/image/fetch/$s_!EmdW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f95dd1b-860d-4381-a752-9ca5f4be6d90_1836x1324.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Rebuno&#8217;s execution dashboard shows durable agent steps, approvals, and execution status.</span></p><ul><li><p><a href="https://github.com/rebuno/rebuno">rebuno</a>: a library for adding durable step-level execution to agents: every tool and LLM call is recorded to a Postgres-backed event log, interrupted runs resume from the last recorded step, and YAML guardrails allow, deny, or hold any step for human approval. It works with LangChain, CrewAI, Pydantic AI, Mastra, and Vercel AI SDK.</p></li><li><p><a href="https://github.com/pizza-bot-app/pizza-bot">pizza-bot</a>: an inbox for long-running AI agents. It puts work items that wait on your decision in Unread, but can continue to run. It&#8217;s something I wanted to build in <a href="https://github.com/alexeygrigorev/codehive">CodeHive</a>, but never managed.</p></li><li><p><a href="https://github.com/ordewell/ordewell">ordewell</a>: a multi-agent task orchestrator that turns one goal into an ordered plan of coding-agent tasks, each with its own runner, model, thinking effort, and mode.</p></li><li><p><a href="https://github.com/greentfrapp/panel">panel</a>: a research workspace where the agent works together with you. It shows all the files, PDFs, and notebooks in one dock. You interact with the agent in the same environment and don&#8217;t need back-and-forth switching.</p></li></ul><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[AI Engineering Buildcamp Starts Next Week]]></title><description><![CDATA[Next Monday (September 21), I start the next cohort AI Engineering Buildcamp. It&#8217;s going to be the forth cohort of the course.]]></description><link>https://aishippingblog.com/p/ai-engineering-buildcamp-starts-next</link><guid isPermaLink="false">https://aishippingblog.com/p/ai-engineering-buildcamp-starts-next</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Mon, 14 Sep 2026 17:21:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CsUw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Next Monday (September 21), I start the next cohort <a href="https://maven.com/alexey-grigorev/from-rag-to-agents">AI Engineering Buildcamp</a>. It&#8217;s going to be the forth cohort of the course.</p><p>The course is a practical and hands-on introduction to AI Engineering. After finishing the course, you&#8217;ll be able to create a project independently with tests, monitoring and a proper evaluation set. There are no slides in the course. Instead, we focus on coding a lot - also with help of AI assistants.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>I selected the topics for the course to give you a solid foundation to work as an AI Engineer and be able to integrate AI into your product.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CsUw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CsUw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 424w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 848w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 1272w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CsUw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png" width="1456" height="683" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:683,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI Engineering Buildcamp course page showing 4.7 rating and Sep 21 to Nov 22 cohort dates&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI Engineering Buildcamp course page showing 4.7 rating and Sep 21 to Nov 22 cohort dates" title="AI Engineering Buildcamp course page showing 4.7 rating and Sep 21 to Nov 22 cohort dates" srcset="https://substackcdn.com/image/fetch/$s_!CsUw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 424w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 848w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 1272w, https://substackcdn.com/image/fetch/$s_!CsUw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe30a1510-7b48-4e8f-9b58-1dffc0105185_2772x1300.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">AI Engineering Buildcamp cohort 4 runs Sep 21 to Nov 22, 2026</figcaption></figure></div><p>In this post, I&#8217;ll talk more about the course content, the projects from past cohorts, what participants say, and how to join.</p><p>In particular, I&#8217;ll cover:</p><ul><li><p>What is AI Engineering Buildcamp</p></li><li><p>The running example, optional projects, and homework</p></li><li><p>How the capstone works</p></li><li><p>What people in the past cohorts built</p></li><li><p>What participants say</p></li><li><p>How to join Cohort 4</p></li></ul><p></p><h2><strong>AI Engineering Buildcamp</strong></h2><p>In AI Engineering Buildcamp you learn AI engineering by building. We take one step at at a time, and at the end arrive at a fully-working system.</p><p>You&#8217;ll work on many projects during the course:</p><ul><li><p>The key running example project that evolves through the course - the documentation agent.</p></li><li><p>Each week, there will be a new homework project. You will have to complete it independently to solidify the learning from the lessons.</p></li><li><p>Optional example projects that show how the same ideas apply to other RAG and agent use cases</p></li><li><p>Your capstone project, where you apply everything to a use case you choose</p></li></ul><p></p><h2><strong>1. A Running Example Project</strong></h2><p>The running example through the course is the Documentation Agent. We index the Evidently documentation and build an AI agent on top of it.</p><p>This is a very practical application. Many companies have internal knowledge databases and need to be able to access them. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ycBZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ycBZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 424w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 848w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 1272w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ycBZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png" width="1456" height="704" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:704,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Docs Assistant answering from Evidently documentation with agent activity and cited files&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Docs Assistant answering from Evidently documentation with agent activity and cited files" title="Docs Assistant answering from Evidently documentation with agent activity and cited files" srcset="https://substackcdn.com/image/fetch/$s_!ycBZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 424w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 848w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 1272w, https://substackcdn.com/image/fetch/$s_!ycBZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd763b8c0-5c74-4840-8976-c62f0127d3bf_1804x872.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Docs Assistant answers from Evidently docs with search activity and cited files</figcaption></figure></div><p>We start from RAG and add on top of it each week:</p><ul><li><p>Week 1 lays the foundation. It covers LLMs, the OpenAI API, RAG, and search. We index the docs and build a first RAG app to answer questions about them. From week 1, you already have a usable AI system.</p></li><li><p>Week 2 helps you catch up on week 1 materials. It adds more examples to strengthen your understanding of RAG.</p></li><li><p>Week 3 adds agentic capabilities. We turn the RAG app into an agent that can explore the docs database.</p></li><li><p>Week 4 focuses on reliability. We add tests to verify that the agent works as intended. This includes classical unit tests and LLM-based judges.</p></li><li><p>Week 5 focuses on monitoring. You see how the agent behaves in production. You collect the info you need to observe and debug it.</p></li><li><p>Week 6 adds systematic evaluation. You learn to evaluate agents with a human in the loop. You create judges that mimic human evaluators and use synthetic data.</p></li></ul><p>After six week, we&#8217;ll go through the same progression as any serious AI application: from simple RAG to a tool-using agent that we test, monitor and evaluate.</p><h2><strong>2. Optional Projects</strong></h2><p>If the main example is not enough, there are more optional projects that you can learn from.</p><p>For RAG, we&#8217;ll cover:</p><ul><li><p>FAQ Assistant: a chatbot for course FAQ data that uses RAG with boosting and filtering</p></li><li><p>YouTube Transcript Summarizer: a system that extracts summaries and chapter structure from YouTube videos with structured output</p></li><li><p>PDF Book Processor: a workflow for parsing a complex PDF, such as a book with math formulas and non-trivial layout. This kind of task often appears in take-home assignments and interviews.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SaES!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SaES!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 424w, https://substackcdn.com/image/fetch/$s_!SaES!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 848w, https://substackcdn.com/image/fetch/$s_!SaES!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 1272w, https://substackcdn.com/image/fetch/$s_!SaES!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SaES!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png" width="1456" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66368a6d-1035-4958-942f-867360f2afbf_1727x911.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;PDF Book Processor notebook parsing a math book with PyMuPDF and page image encoding&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="PDF Book Processor notebook parsing a math book with PyMuPDF and page image encoding" title="PDF Book Processor notebook parsing a math book with PyMuPDF and page image encoding" srcset="https://substackcdn.com/image/fetch/$s_!SaES!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 424w, https://substackcdn.com/image/fetch/$s_!SaES!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 848w, https://substackcdn.com/image/fetch/$s_!SaES!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 1272w, https://substackcdn.com/image/fetch/$s_!SaES!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66368a6d-1035-4958-942f-867360f2afbf_1727x911.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">PDF Book Processor parses a math book page by page for RAG</figcaption></figure></div><p>We&#8217;ll have a few agent applications too:</p><ul><li><p>Web Search Agent: an agent that uses web search tools to find and filter and synthesize info from the internet</p></li><li><p>YouTube Researcher: an agent that searches YouTube and fetches transcripts and produces structured research summaries</p></li><li><p>Coding Agent: a functional coding agent that scaffolds Django applications</p></li><li><p>Code Analysis Agent: an agent for analyzing and understanding codebases</p></li><li><p>Deep Research Agent: a multi-stage research system that starts with broad search and expands into follow-up queries. It goes deeper into promising directions and fact-checks findings and generates a final article.</p></li></ul><h2><strong>3. Homework Projects</strong></h2><p>The core weeks (weeks 1-6) end with a separate homework mini-project.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CtLg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CtLg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 424w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 848w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 1272w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CtLg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png" width="1456" height="1018" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1018,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Homework mini-project building a SQL agent over NYC taxi data with tests and LLM judges&quot;,&quot;title&quot;:&quot;Homework mini-project building a SQL agent over NYC taxi data with tests and LLM judges&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Homework mini-project building a SQL agent over NYC taxi data with tests and LLM judges" title="Homework mini-project building a SQL agent over NYC taxi data with tests and LLM judges" srcset="https://substackcdn.com/image/fetch/$s_!CtLg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 424w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 848w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 1272w, https://substackcdn.com/image/fetch/$s_!CtLg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c66712-66c3-4cfc-9ce1-f3d66ff2b6bb_1722x1204.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Homework mini-project with a SQL agent over NYC taxi data</figcaption></figure></div><p></p><p>To solidify what you learned in the lessons, you&#8217;ll need to build another system yourself:</p><ul><li><p>Weeks 1 to 2: document processing with AI. You download books and extract PDF text and chunk docs and build a full RAG pipeline.</p></li><li><p>Week 3: Wikipedia Agent. You implement search and page-fetching tools and build an agent with a framework of your choice.</p></li><li><p>Week 4: testing through the DuckDB SQL Agent. You build an agent that translates natural language into SQL. You write pytest tests and add LLM judges and track costs.</p></li><li><p>Week 5: Trivia Quizmaster Agent. You build an interactive trivia system and add monitoring it.</p></li><li><p>Week 6: systematic evaluation through the Recipe Assistant Evaluation. You design 20 plus evaluation scenarios and run batch tests and detect hallucinations.</p></li></ul><p></p><h2><strong>4. Capstone Project</strong></h2><p>On top of that, you work on your capstone during the course.</p><p>I want to help you to start with your capstone from day one and start building your project layer by layer.:</p><ul><li><p>Weeks 1 to 2: define the use case and build the first working version with RAG</p></li><li><p>Week 3: make it agentic by adding tools. At this stage, your project moves beyond answering questions. It becomes a system that can make decisions.</p></li><li><p>Week 4: make it reliable by adding testing</p></li><li><p>Week 5: monitor it and start collecting logs from user interactions</p></li><li><p>Week 6: evaluate the project based on collected interactions and user scenarios you generated</p></li><li><p>Weeks 7 and 8: polish and extend and deploy the project</p></li><li><p>Week 9: present the result and get peer feedback and continue developing it</p></li></ul><p>My main focus for this cohort will be to make sure more people finish with a project. In the previous cohorts, some participants waited too long to decide what to build, and in the end they run out of time without submitting a project.</p><p>I want to address that directly. I will encourage to work on your projects from day 1.</p><p>If you already have an idea, I&#8217;ll share a framework and a set of instructions for your coding agent to implement it as quickly as possible.</p><p>If you don&#8217;t have an idea yet, I&#8217;ll share my design thinking approach to help you find something meaningful to solve.</p><p>I already used this approach in the last cohort, and one of the course graduates, Amar Agrawal, created a special app based on this approach: <a href="https://project-ideation-tool.streamlit.app/">Project Ideation Tool</a>.</p><p></p><h2><strong>Certificate</strong></h2><p>If you finish the project and do 3 peer reviews, you will get a certificate signed by me.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gQQk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gQQk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gQQk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Sample AI Engineering Buildcamp certificate of completion&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Sample AI Engineering Buildcamp certificate of completion" title="Sample AI Engineering Buildcamp certificate of completion" srcset="https://substackcdn.com/image/fetch/$s_!gQQk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gQQk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d0c4a37-9b14-439a-97e8-02b7e475ff6f_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Sample certificate of completion for AI Engineering Buildcamp</figcaption></figure></div><p>The certificate confirms that you shipped a capstone.</p><h2><strong>Past projects</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZJX6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZJX6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 424w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 848w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 1272w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZJX6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png" width="1456" height="1048" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1048,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Collage of 12 cohort 3 capstone project interfaces&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Collage of 12 cohort 3 capstone project interfaces" title="Collage of 12 cohort 3 capstone project interfaces" srcset="https://substackcdn.com/image/fetch/$s_!ZJX6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 424w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 848w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 1272w, https://substackcdn.com/image/fetch/$s_!ZJX6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f1ed4c1-749f-41e1-8902-9eb4ae325e8c_1456x1048.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Cohort 3 capstones, from exam generation and job tools to chess coaching and learning apps</figcaption></figure></div><p>I have already described what people from the past cohorts build.</p><p>Here are the articles:</p><ul><li><p>Cohort 1: <a href="https://aishippingblog.com/p/5-ideas-for-ai-agents-and-openais">5 ideas for AI agents</a>.</p></li><li><p>Cohort 2: <a href="https://aishippingblog.com/p/9-real-life-ai-projects-from-ai-engineering">9 Real-Life AI Projects from AI Engineering Buildcamp Graduates</a>.</p></li><li><p>Cohort 3: <a href="https://aishippingblog.com/p/13-ai-projects-from-ai-engineering">13 AI Projects from AI Engineering Buildcamp</a>.</p></li></ul><p>I&#8217;m looking forward to seeing what you will build in Cohort 4!</p><h2><strong>What participants say</strong></h2><p>Here is what past participants say about the course.</p><p>Vishnu, Data Scientist at Humana, joined cohort 2.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!siKq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!siKq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 424w, https://substackcdn.com/image/fetch/$s_!siKq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 848w, https://substackcdn.com/image/fetch/$s_!siKq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 1272w, https://substackcdn.com/image/fetch/$s_!siKq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!siKq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png" width="1130" height="544" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:544,&quot;width&quot;:1130,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Vishnu review from April 2026 praising week-one project start and help with shaping ideas&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Vishnu review from April 2026 praising week-one project start and help with shaping ideas" title="Vishnu review from April 2026 praising week-one project start and help with shaping ideas" srcset="https://substackcdn.com/image/fetch/$s_!siKq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 424w, https://substackcdn.com/image/fetch/$s_!siKq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 848w, https://substackcdn.com/image/fetch/$s_!siKq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 1272w, https://substackcdn.com/image/fetch/$s_!siKq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e777ccf-7185-40a6-91e4-bd5e4d5238df_1130x544.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Vishnu on starting in week one and shaping the project idea</figcaption></figure></div><p>Carmine, Senior Coach, Data Science and ML Engineer, joined cohort 3.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jKpR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jKpR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 424w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 848w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 1272w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jKpR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png" width="1112" height="824" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:824,&quot;width&quot;:1112,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Carmine review from June 2026 praising structure and building from day one&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Carmine review from June 2026 praising structure and building from day one" title="Carmine review from June 2026 praising structure and building from day one" srcset="https://substackcdn.com/image/fetch/$s_!jKpR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 424w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 848w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 1272w, https://substackcdn.com/image/fetch/$s_!jKpR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4845932f-91bf-42eb-bf4a-bf26f3c55242_1112x824.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Carmine on structure and building from day one</figcaption></figure></div><p>Lars, Data Scientist at NS, joined cohort 3.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!En8O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!En8O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 424w, https://substackcdn.com/image/fetch/$s_!En8O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 848w, https://substackcdn.com/image/fetch/$s_!En8O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!En8O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!En8O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png" width="1112" height="532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:1112,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Lars review from July 2026 praising structure and project-based learning&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Lars review from July 2026 praising structure and project-based learning" title="Lars review from July 2026 praising structure and project-based learning" srcset="https://substackcdn.com/image/fetch/$s_!En8O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 424w, https://substackcdn.com/image/fetch/$s_!En8O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 848w, https://substackcdn.com/image/fetch/$s_!En8O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!En8O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682a77c8-c091-43c3-8e99-dfa5627e3680_1112x532.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Lars on learning RAG and agents through his own project</figcaption></figure></div><p></p><p>You can find more reviews in the testimonial section of <a href="https://maven.com/alexey-grigorev/from-rag-to-agents">the course page</a>.</p><h2><strong>Weekly Workload</strong></h2><p>The course lasts about nine weeks. It runs September 21 to November 22, 2026. It is designed for working professionals, but it still requires a meaningful commitment.</p><p>Most of the work is asynchronous. Each week features pre-recorded lessons and practical exercises and homework. Depending on your background and pace, this usually takes 3 to 10 hours per week. Project work takes 10 to 20 hours per week, distributed through the course.</p><p>There is one live session each week, mainly for office hours. This is when you can ask technical questions and discuss architecture decisions and receive feedback on your projects from me. I record these sessions and share them with everyone, along with a written summary.</p><p>This is not a passive course. It is intended for people who want to spend real time building and debugging, rather than just watching explanations and taking notes.</p><h2><strong>Scholarship</strong></h2><p>The scholarship form closed today. I received over 2,100 submissions. Selecting recipients was extremely difficult.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pIuO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pIuO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 424w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 848w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 1272w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pIuO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png" width="1456" height="376" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:376,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Scholarship application form for cohort 4 showing 2,143 responses&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Scholarship application form for cohort 4 showing 2,143 responses" title="Scholarship application form for cohort 4 showing 2,143 responses" srcset="https://substackcdn.com/image/fetch/$s_!pIuO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 424w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 848w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 1272w, https://substackcdn.com/image/fetch/$s_!pIuO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb84147e-a9c5-47c4-aaa6-dcb4aa1050f5_1768x456.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Scholarship applications for cohort 4 closed with 2,143 responses</figcaption></figure></div><p>Today I contacted the people who were selected. If you do not see an email from me, you were not selected this time. I can not write to everyone individually.</p><p>If you applied and opted in for updates, I added you to this newsletter. That is why you see this email now. Thanks for all your applications.</p><p>When deciding, I focused on existing impact. I looked for people already doing something and helping their local community, and I prioritized regions where the course price is hard to afford. </p><h2><strong>Ways to Join</strong></h2><p>The standard price of the course is 1,799 USD.</p><ol><li><p>You can use your company learning budget to cover the cost. I wrote a <a href="https://aishippinglabs.com/blog/how-to-join-ai-engineering-buildcamp">short guide</a> with an email template you can use to reach out to your manager. Maven also has <a href="https://maven.com/expense">their own guide</a>.</p></li><li><p>You can get a team discount. Teams of 2 to 9 get 20 percent off. Teams of 10 plus get 25 percent off.</p></li><li><p>I also provide full and partial scholarships for each cohort. For cohort 4, the form is already closed. See the Scholarship section above for details.</p></li><li><p>If none of the options above work for you, you can use the &#8220;SUBSTACK&#8221; code to get a 20% discount.</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://maven.com/alexey-grigorev/from-rag-to-agents&quot;,&quot;text&quot;:&quot;Enroll now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://maven.com/alexey-grigorev/from-rag-to-agents"><span>Enroll now</span></a></p><p>If you need a student discount or PPP pricing or have any questions about the joining options, contact me at <a href="mailto:alexey@datatalks.club">alexey@datatalks.club</a>, and I&#8217;ll help you.</p><h2><strong>Join the Course</strong></h2><p>AI Engineering Buildcamp is designed for hands-on learning. You will develop your own capstone project and complete mini-projects for homework and engage in optional practical projects. You will gain experience with RAG and agents and testing and observability and evaluation and deployment.</p><p>If you have been thinking about joining, registration is open until September 21.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Free and Affordable AI-Native Development]]></title><description><![CDATA[Build more, learn more!]]></description><link>https://aishippingblog.com/p/free-and-affordable-ai-native-development</link><guid isPermaLink="false">https://aishippingblog.com/p/free-and-affordable-ai-native-development</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 11 Sep 2026 23:02:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zfRh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently posted on X that we have access to so many tokens that I&#8217;m running out of ideas what to build.</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/Al_Grigor/status/2096543638687756663&quot;,&quot;full_text&quot;:&quot;Now we can have\n\n- 3 codex resets\n- free Muse 3 on OpenCode Zen \n- Very cheap Muse 3 and Omen Alpha on OpenCode Go\n- Unlimited glm-5.3-flash on <a class=\&quot;tweet-url\&quot; href=\&quot;https://z.ai\&quot;>z.ai</a> plans from 5 pm every day for 10 hours\n- Hy3 free on multiple platforms \n\nI'm running out of things to build&#8230;&quot;,&quot;username&quot;:&quot;Al_Grigor&quot;,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/1793934377547644928/n0X1VyYw_normal.jpg&quot;,&quot;date&quot;:&quot;2026-09-06T10:18:50.000Z&quot;,&quot;photos&quot;:[],&quot;quoted_tweet&quot;:{&quot;full_text&quot;:&quot;what do I do to have unlimited tokens&quot;,&quot;username&quot;:&quot;enjojoyy&quot;,&quot;name&quot;:&quot;albina&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/1997389601216880640/o6jlOaId_normal.jpg&quot;},&quot;reply_count&quot;:89,&quot;retweet_count&quot;:147,&quot;like_count&quot;:3314,&quot;impression_count&quot;:237432,&quot;expanded_url&quot;:null,&quot;video_url&quot;:null,&quot;video_preview_media_key&quot;:null,&quot;belowTheFold&quot;:false}" data-component-name="Twitter2ToDOM"></div><p>At DataTalks.Club we&#8217;re running <a href="https://aishippingblog.com/p/ai-dev-tools-zoomcamp-2026-starts">AI Dev Tools Zoomcamp</a> and people are asking me where they can get access to coding agents for free or without paying a lot of money. So I figured I should take that post from X and turn it into an article.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>While I do recommend getting a paid Codex or Claude subscription and use it for the course, there are some other providers that can get you a lot of tokens for less money or even for free.</p><p>In this post, I&#8217;ll share some of them. We&#8217;ll cover</p><ul><li><p>Free coding plans</p></li><li><p>Cheap coding plans</p></li><li><p>Current promotions</p></li></ul><p>Let&#8217;s go!</p><h2><strong>Free coding plans</strong></h2><p>There are multiple platforms that currently offer free coding plans.</p><p>One of them is Tencent. They recently released Hy3 and now offer a free tier through its <a href="https://www.workbuddy.ai/">WorkBuddy</a> and <a href="https://www.codebuddy.ai/">CodeBuddy</a> assistants. It&#8217;s not available in Germany, so I couldn&#8217;t try it - but maybe it&#8217;ll work for you.</p><p>Another one is <a href="https://opencode.ai/zen">OpenCode Zen</a>. They have a variety of models absolutely for free. One of these models is Muse Spark 1.3 from Meta.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RJSN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RJSN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 424w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 848w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 1272w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RJSN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png" width="1456" height="865" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:865,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OpenCode Zen pricing table showing free models including Big Pickle, MiMo-V2.5, Ling 3.0 Flash, Nemotron 3 Ultra, Nemotron 3.5 Lightning, and Muse Spark 1.3 Contributor Free, all with free input, output and cached read pricing&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OpenCode Zen pricing table showing free models including Big Pickle, MiMo-V2.5, Ling 3.0 Flash, Nemotron 3 Ultra, Nemotron 3.5 Lightning, and Muse Spark 1.3 Contributor Free, all with free input, output and cached read pricing" title="OpenCode Zen pricing table showing free models including Big Pickle, MiMo-V2.5, Ling 3.0 Flash, Nemotron 3 Ultra, Nemotron 3.5 Lightning, and Muse Spark 1.3 Contributor Free, all with free input, output and cached read pricing" srcset="https://substackcdn.com/image/fetch/$s_!RJSN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 424w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 848w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 1272w, https://substackcdn.com/image/fetch/$s_!RJSN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab1f7959-5176-4e35-925f-42aa09ea5ba1_1478x878.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">OpenCode Zen free models - Muse Spark 1.3 Contributor Free included</figcaption></figure></div><p>The full name for this model is &#8220;Muse Spark 1.3 Contributor Free&#8221;. The &#8220;contributor&#8221; part here means that you&#8217;re &#8220;contributing&#8221; your data to Meta. That is, they can use your data to improve their model. That&#8217;s why it&#8217;s free - you pay for it with your data.</p><p>For me it&#8217;s not a concern. It&#8217;s quite a powerful model, it feels like Opus 5 level. I can have one or two heavy coding sessions with Muse 1.3 in OpenCode Zen absolutely for free.</p><p>Once I hit the limits, I can stay in OpenCode and switch the session to OpenCode Go - their coding plan.</p><h2><strong>Affordable coding plans</strong></h2><p><a href="https://opencode.ai/go">OpenCode Go</a> costs $10 per month. For this $10 you get a lot. Specifically, you get the same Muse 1.3 Contributor, although without the &#8220;free&#8221; part.</p><p>Again, it&#8217;s a contributor model, so Meta is subsidizing 90% of the cost - at least this is what they say. And you can get a lot done with this model. Combine it with 1-2 free sessions from OpenCode Zen, and you&#8217;ll get 5+ hours of work per day with this plan.</p><p>And there are other models, like DeepSeek V4.1 Flash, which currently offers 75% discount (i.e. 4x more usage).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E9Fe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E9Fe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 424w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 848w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E9Fe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png" width="1456" height="863" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:863,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OpenCode Go usage chart showing estimated requests per 5 hours: Kimi K3 at 110, Kimi K2.7 Code at 1,350, GPT 5.6 Luna at 2,050, MiniMax M3 at 3,200, Qwen3.7 Plus at 4,300, GLM-5.3-Flash at 6,320, DeepSeek V4 Flash at 13,000, DeepSeek V4.1 Flash at 26,000 with 4x usage tag, MiMo-V2.5 at 30,100, Muse Spark 1.3 Contributor at 45,300&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OpenCode Go usage chart showing estimated requests per 5 hours: Kimi K3 at 110, Kimi K2.7 Code at 1,350, GPT 5.6 Luna at 2,050, MiniMax M3 at 3,200, Qwen3.7 Plus at 4,300, GLM-5.3-Flash at 6,320, DeepSeek V4 Flash at 13,000, DeepSeek V4.1 Flash at 26,000 with 4x usage tag, MiMo-V2.5 at 30,100, Muse Spark 1.3 Contributor at 45,300" title="OpenCode Go usage chart showing estimated requests per 5 hours: Kimi K3 at 110, Kimi K2.7 Code at 1,350, GPT 5.6 Luna at 2,050, MiniMax M3 at 3,200, Qwen3.7 Plus at 4,300, GLM-5.3-Flash at 6,320, DeepSeek V4 Flash at 13,000, DeepSeek V4.1 Flash at 26,000 with 4x usage tag, MiMo-V2.5 at 30,100, Muse Spark 1.3 Contributor at 45,300" srcset="https://substackcdn.com/image/fetch/$s_!E9Fe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 424w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 848w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!E9Fe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F589284fc-ee3f-41c9-8224-12b85d8ea66d_2004x1188.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">OpenCode Go usage estimates - DeepSeek V4.1 Flash at 26,000 requests per 5 hours with 4x usage, Muse Spark 1.3 Contributor at 45,300</figcaption></figure></div><p>I used to recommend GitHub Copilot as the first coding plan. Not anymore - now it&#8217;s OpenCode Go.</p><p>Other cheap alternatives in the same $8-20 range:</p><ul><li><p><a href="https://openai.com/chatgpt/pricing/">ChatGPT Plus</a> ($20)</p></li><li><p><a href="https://z.ai/subscribe">Z.ai GLM Coding Plan Lite</a> ($18)</p></li><li><p><a href="https://platform.minimax.io/subscribe/token-plan">MiniMax Token Plan</a> ($20)</p></li><li><p><a href="https://commandcode.ai/docs/plans/goat">Command Code GOAT</a> ($10)</p></li></ul><p>Prices and quotas change fast, so check the current plan pages.</p><h2><strong>Cheap API access</strong></h2><p>So far we talked about coding plans. Instead, you can pay for API calls yourself and plug any API LLM platform to your coding agent.</p><p>If you do that directly with OpenAI or Anthropic and put $5 into your account, they will disappear before you can say &#8220;hello&#8221; to your coding agent.</p><p>But if you use <a href="https://api-docs.deepseek.com/quick_start/pricing">DeepSeek&#8217;s V4.1 Flash</a> during off-peak hours, you&#8217;ll be getting millions of tokens, which is enough to do multiple coding sessions.</p><p>Also, <a href="https://openrouter.ai/discover">OpenRouter</a> gives access to many free and cheap models. Any model ID ending in <code>:free</code> costs zero tokens. The rate limits are very strict though, but once you pay, the limits are lifted.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vvgC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vvgC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 424w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 848w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 1272w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vvgC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png" width="1254" height="836" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:836,&quot;width&quot;:1254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OpenRouter most-used free models ranking by weekly tokens: Nemotron 3 Ultra free at 3.6T tokens per week with 1M context, Laguna S 2.1 free at 1.2T with 262K context, Nemotron 3.5 Lightning free at 958.4B with 1M context, Ling 3.0 Flash Fin free at 881.5B with 262K context, Dots3-Note Preview free at 397.9B with 512K context&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OpenRouter most-used free models ranking by weekly tokens: Nemotron 3 Ultra free at 3.6T tokens per week with 1M context, Laguna S 2.1 free at 1.2T with 262K context, Nemotron 3.5 Lightning free at 958.4B with 1M context, Ling 3.0 Flash Fin free at 881.5B with 262K context, Dots3-Note Preview free at 397.9B with 512K context" title="OpenRouter most-used free models ranking by weekly tokens: Nemotron 3 Ultra free at 3.6T tokens per week with 1M context, Laguna S 2.1 free at 1.2T with 262K context, Nemotron 3.5 Lightning free at 958.4B with 1M context, Ling 3.0 Flash Fin free at 881.5B with 262K context, Dots3-Note Preview free at 397.9B with 512K context" srcset="https://substackcdn.com/image/fetch/$s_!vvgC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 424w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 848w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 1272w, https://substackcdn.com/image/fetch/$s_!vvgC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff732ec7f-e515-4763-8dd6-3d30ea98838c_1254x836.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Most-used free models on OpenRouter by weekly tokens - Nemotron 3 Ultra leads at 3.6T.</figcaption></figure></div><h2><strong>Current promotions</strong></h2><p>I really like Z.ai. I have used them since September last year through their API, and I&#8217;ve been using their coding plan since December.</p><p>Now they are regularly running promotions and giving away 300 million tokens for glm-5.3-flash. You have to use their <a href="https://zcode.z.ai/en">Zcode harness</a>, and the promotions are typically timed for weekends. It&#8217;s a lot of tokens - many people complain that these tokens expire before they can spend them.</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/zcode_ai/status/2098396306750517317&quot;,&quot;full_text&quot;:&quot;WEEKEND BUILD V is arriving &#129370;&#10024;\n300M tokens per person, free.Bring your next idea to life.\nSep 11, 8 AM &#8211; Sep 13, 6 PM PDT.\nTokens activate when the event starts.\nUpdate ZCode to the latest version, then tap the bottom-left event card when it opens.\n<a class=\&quot;tweet-url\&quot; href=\&quot;https://zcode.z.ai\&quot;>zcode.z.ai</a> &quot;,&quot;username&quot;:&quot;zcode_ai&quot;,&quot;name&quot;:&quot;ZCode&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/2066234804325289984/oEcGryOS_normal.jpg&quot;,&quot;date&quot;:&quot;2026-09-11T13:00:40.000Z&quot;,&quot;photos&quot;:[{&quot;img_url&quot;:&quot;https://pbs.substack.com/media/HR7-jS8asAAFwVi.jpg&quot;,&quot;link_url&quot;:&quot;https://t.co/nsfg7NtxQx&quot;}],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:113,&quot;retweet_count&quot;:24,&quot;like_count&quot;:400,&quot;impression_count&quot;:34469,&quot;expanded_url&quot;:null,&quot;video_url&quot;:null,&quot;video_preview_media_key&quot;:null,&quot;belowTheFold&quot;:true}" data-component-name="Twitter2ToDOM"></div><p>Additionally, right now they are running <a href="https://docs.z.ai/devpack/notice/event-glm-5.3-flash">a promo for all Z.ai coding plan subscribers</a>. If you&#8217;re one of them, you get unlimited glm-5.3-flash tokens from 5pm to 3am CET.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rC9N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rC9N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 424w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 848w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 1272w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rC9N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png" width="1418" height="590" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:590,&quot;width&quot;:1418,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Z.ai campaign details: every day from 23:00 to 09:00 the following day, GLM-5.3-Flash through GLM Coding Plan - use via ZCode means zero quota consumption for unlimited usage, use via other supported agents means available quota is doubled&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Z.ai campaign details: every day from 23:00 to 09:00 the following day, GLM-5.3-Flash through GLM Coding Plan - use via ZCode means zero quota consumption for unlimited usage, use via other supported agents means available quota is doubled" title="Z.ai campaign details: every day from 23:00 to 09:00 the following day, GLM-5.3-Flash through GLM Coding Plan - use via ZCode means zero quota consumption for unlimited usage, use via other supported agents means available quota is doubled" srcset="https://substackcdn.com/image/fetch/$s_!rC9N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 424w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 848w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 1272w, https://substackcdn.com/image/fetch/$s_!rC9N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72a9099-9936-4c07-8a09-4077bcd136e9_1418x590.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Z.ai evening promo rules - zero quota consumption via ZCode, doubled quota via other agents (time in SG timezone)</figcaption></figure></div><p>Well, they aren&#8217;t completely &#8220;unlimited&#8221; - there are still rate-limits. But I don&#8217;t hit these limits unless I run 20+ agents in parallel.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zfRh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zfRh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 424w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 848w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 1272w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zfRh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png" width="1456" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Z.ai usage trends bar chart for September 5-11 showing total 9.7B tokens, almost all GLM-5.3-Flash at 9.7B with GLM-5.3 at only 10.5M tokens&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Z.ai usage trends bar chart for September 5-11 showing total 9.7B tokens, almost all GLM-5.3-Flash at 9.7B with GLM-5.3 at only 10.5M tokens" title="Z.ai usage trends bar chart for September 5-11 showing total 9.7B tokens, almost all GLM-5.3-Flash at 9.7B with GLM-5.3 at only 10.5M tokens" srcset="https://substackcdn.com/image/fetch/$s_!zfRh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 424w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 848w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 1272w, https://substackcdn.com/image/fetch/$s_!zfRh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9baf1c0-2cf2-4974-86f0-492df3a7d22a_1738x898.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">My Z.ai usage for the week - 9.7B GLM-5.3-Flash tokens</figcaption></figure></div><p>That plus 300m tokens plus their quota reduction if you use Zcode - that combined gives you a lot of tokens.</p><p>One downside of Zcode is that it&#8217;s GUI only, no TUI. But I figured out how to <a href="https://github.com/alexeygrigorev/codex-zcode/">connect it to Codex</a>, so I can run Zcode from my terminal.</p><p>Z.ai is not the only company that runs promos. There are others:</p><ul><li><p>Tencent Cloud TokenHub gives every new account 1M free tokens per model, covering DeepSeek V4 Pro/Flash, GLM-5, MiniMax M3 and Kimi K2.6.</p></li><li><p>Verdant currently offers GLM 5.3 Flash and DeepSeek V4 Flash for free within 5-hour and weekly limits.</p></li><li><p>Cursor doubled included usage for its first-party models (Auto, Composer 2.5) on paid plans.</p></li><li><p>Cognition (the company behind Devin) just released a new model SWE-2 and they made it unlimited in Devin paid plans.</p></li></ul><h2><strong>Build more</strong></h2><p>With all these free models and promotions I no longer have a problem of having no tokens. My main problem is deciding what to build next.</p><p>I hope you&#8217;ll have the same problem. Have fun and enjoy building!</p><h2><strong>Tools</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a7yQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a7yQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 424w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 848w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a7yQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png" width="1456" height="902" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:902,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OtoDock GitHub hero: blue robot logo, 'OtoDock - Collaborative Agents', tagline 'The agentic company OS', subtitle 'The brains of your company, built on Claude Code and Codex, working on your Anthropic and OpenAI subscriptions', badges for FSL-1.1-Apache-2.0 license, version 1.6.0, passing CI, docs and website links&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OtoDock GitHub hero: blue robot logo, 'OtoDock - Collaborative Agents', tagline 'The agentic company OS', subtitle 'The brains of your company, built on Claude Code and Codex, working on your Anthropic and OpenAI subscriptions', badges for FSL-1.1-Apache-2.0 license, version 1.6.0, passing CI, docs and website links" title="OtoDock GitHub hero: blue robot logo, 'OtoDock - Collaborative Agents', tagline 'The agentic company OS', subtitle 'The brains of your company, built on Claude Code and Codex, working on your Anthropic and OpenAI subscriptions', badges for FSL-1.1-Apache-2.0 license, version 1.6.0, passing CI, docs and website links" srcset="https://substackcdn.com/image/fetch/$s_!a7yQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 424w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 848w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!a7yQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc620908b-f3f3-424d-8e61-945fbe248aa6_1872x1160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I haven&#8217;t included the section with new tools for quite some time. Now I want to fix it.</p><ul><li><p><strong><a href="https://github.com/timgordontg/engrim">engrim</a></strong>: a local-first SQLite memory engine that keeps one project-scoped episodic store across Antigravity, Claude Code, Cursor, Windsurf, and Codex. With their approach it&#8217;s very easy to switch models in the middle of a session, and you don&#8217;t need to send the whole session transcript when doing it.</p></li><li><p><strong><a href="https://github.com/OtoDock/oto-dock">oto-dock</a></strong>: &#8220;The brains of your company, built on Claude Code &amp; Codex, working on your Anthropic and OpenAI subscriptions.&#8221; It&#8217;s a self-hosted company OS where agents organized in departments connect to company tools, delegate to each other, and keep working unattended. I&#8217;m experimenting with automating as much of DataTalks.Club ops as I can, so this project looks promising.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[13 AI Projects from AI Engineering Buildcamp Cohort 3]]></title><description><![CDATA[See what the course participants built]]></description><link>https://aishippingblog.com/p/13-ai-projects-from-ai-engineering</link><guid isPermaLink="false">https://aishippingblog.com/p/13-ai-projects-from-ai-engineering</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 04 Sep 2026 18:21:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/04db2d9f-7355-49f4-bce5-58a862c6364e_1456x1048.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The third cohort of my AI Engineering course wrapped this summer, and in this post I want to share what we built.</p><p>The projects are real-life and include an exam questions generator, a chess coach, a RAG pipeline, job tools, and research assistants.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is not the first post about the projects that AI Buildcamp graduates built. I wrote about the previous two cohorts earlier:</p><ul><li><p>First cohort: <a href="https://aishippingblog.com/p/5-ideas-for-ai-agents-and-openais">5 ideas for AI agents</a></p></li><li><p>Second cohort: <a href="https://aishippingblog.com/p/9-real-life-ai-projects-from-ai-engineering">9 Real-Life AI Projects from AI Engineering Buildcamp Graduates</a></p></li></ul><p>In this post I want to describe some of these projects.</p><p>Some of the course participants presented their projects live on <a href="https://youtu.be/kKUDZwdyuP4">Demo Day</a>, so we&#8217;ll start with them.</p><p>Let&#8217;s start!</p><h2><strong>1) Exam Questions Generator by Salma Bouzid</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L-6J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L-6J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 424w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 848w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 1272w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L-6J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Quizgen landing page inviting professors to generate exam questions from their own lecture slides&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Quizgen landing page inviting professors to generate exam questions from their own lecture slides" title="Quizgen landing page inviting professors to generate exam questions from their own lecture slides" srcset="https://substackcdn.com/image/fetch/$s_!L-6J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 424w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 848w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 1272w, https://substackcdn.com/image/fetch/$s_!L-6J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F316ebe9a-9bf3-4d8e-a530-2cee26c77991_1610x977.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Quizgen generates exam questions from the lecture slides</figcaption></figure></div><p>Salma is a data analyst who took the course to get better at building an AI app. She&#8217;s leaving her job to work as a founder. Her project helps university professors generate exams from their own slide decks.</p><p>You upload a course PDF and pick how many questions you want. You also choose a mix of case studies, theory, and applied-concept questions. A professor can approve, edit, or reject each question, then export the exam. In the live demo she asked the model to tailor an HR-management exam to a business student in Berlin.</p><ul><li><p>The frontend is Next.js</p></li><li><p>Supabase stores user actions plus API cost, PDF size, and regeneration prompts</p></li><li><p>She started with Claude and later moved to OpenAI</p></li><li><p>An LLM judge checks that a question is grounded in the course and not answerable without it</p></li></ul><p><a href="https://www.linkedin.com/in/salma-bouzid-45634762/">Salma&#8217;s LinkedIn</a></p><h2><strong>2) ATS Gap Analyser by Amar Agrawal</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mOUn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mOUn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 424w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 848w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 1272w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mOUn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png" width="1140" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1140,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ATS Gap Analyser example result showing a 75 out of 100 moderate match, missing keywords, and improvement suggestions&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="ATS Gap Analyser example result showing a 75 out of 100 moderate match, missing keywords, and improvement suggestions" title="ATS Gap Analyser example result showing a 75 out of 100 moderate match, missing keywords, and improvement suggestions" srcset="https://substackcdn.com/image/fetch/$s_!mOUn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 424w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 848w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 1272w, https://substackcdn.com/image/fetch/$s_!mOUn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fd9d734-6a2e-47a1-a94e-269ca362b9e5_1140x720.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">ATS Gap Analyser&#8217;s example result with a 75/100 match, missing keywords, and improvement suggestions</figcaption></figure></div><p>Amar&#8217;s <a href="https://github.com/Amar-Ag/ats-gap-analyser">ATS Gap Analyser</a> is for people who apply to jobs and never find out what went wrong.</p><p>You paste a job URL and your CV. The app returns a match score out of 100, missing keywords, improvement suggestions, and a three-paragraph cover letter.</p><p>The app is live on <a href="https://ats-gap-analyser.streamlit.app/">Streamlit</a>. It doesn&#8217;t store the CV or the job description, not even in the logs.</p><p>The agent has four tools.</p><ul><li><p>Extract job requirements</p></li><li><p>Score the CV</p></li><li><p>Suggest improvements</p></li><li><p>Generate the cover letter</p></li></ul><p>Suggestions retrieve from 50 ATS best-practice documents with minsearch.</p><ul><li><p>Eval used 50 manually written cases.</p></li><li><p>Version 1 had 60% recall and 30% accuracy, and version 6 reached 88% recall.</p></li><li><p>Amar kept accuracy lower on purpose: he would rather flag a missing skill than miss it.</p></li></ul><p><a href="https://www.linkedin.com/in/amar-agrawal-/">Amar&#8217;s LinkedIn</a></p><h2><strong>3) Chess Coach Agent by Leo Cabibihan</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!klGD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!klGD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 424w, https://substackcdn.com/image/fetch/$s_!klGD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 848w, https://substackcdn.com/image/fetch/$s_!klGD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 1272w, https://substackcdn.com/image/fetch/$s_!klGD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!klGD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png" width="1226" height="835" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:835,&quot;width&quot;:1226,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Chess Coach practice screen showing a chess position, an engine-approved move, and the next scheduled review date&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Chess Coach practice screen showing a chess position, an engine-approved move, and the next scheduled review date" title="Chess Coach practice screen showing a chess position, an engine-approved move, and the next scheduled review date" srcset="https://substackcdn.com/image/fetch/$s_!klGD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 424w, https://substackcdn.com/image/fetch/$s_!klGD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 848w, https://substackcdn.com/image/fetch/$s_!klGD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 1272w, https://substackcdn.com/image/fetch/$s_!klGD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F143b1974-6104-4494-8d6c-7c8ab4afa385_1226x835.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Chess Coach practice screen with a Stockfish-backed move recommendation and a scheduled retry</figcaption></figure></div><p>Leo&#8217;s first project was a chess-coach chat, but it wasn&#8217;t useful. <a href="https://github.com/leo-cabibihan/chess-coach-agent">Chess Coach Agent</a> starts from your real games instead. You import Lichess, Chess.com, or a PGN file. The app finds weak moments, explains them, and drills those positions.</p><p>Leo imported about 2,700 of his own Lichess games for the demo.</p><p>The stack includes FastAPI, React, Postgres with pgvector, and PydanticAI. It also uses Stockfish, BM25 lessons, Logfire, and it&#8217;s <a href="https://chess-coach-agent.onrender.com/">deployed on Render</a>.</p><p>Practice uses spaced repetition at 1, 3, and 7 days, doubling up to 30. If there&#8217;s no API key, deterministic templates still run. CI uses a TestModel so tests stay offline.</p><p><a href="https://www.linkedin.com/in/leo-cabibihan/">Leo&#8217;s LinkedIn</a></p><h2><strong>4) Document Preparation Agent by Paulien Out and Alena Fojtik</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aI9w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aI9w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 424w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 848w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 1272w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aI9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png" width="740" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:740,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ArXiv papers, manuals, and EU documents feed an LLM for parsing, classification, metadata extraction, and chunking before becoming JSON manifests&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="ArXiv papers, manuals, and EU documents feed an LLM for parsing, classification, metadata extraction, and chunking before becoming JSON manifests" title="ArXiv papers, manuals, and EU documents feed an LLM for parsing, classification, metadata extraction, and chunking before becoming JSON manifests" srcset="https://substackcdn.com/image/fetch/$s_!aI9w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 424w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 848w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 1272w, https://substackcdn.com/image/fetch/$s_!aI9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ce8f757-47fe-4f49-9f1a-765972d504e4_740x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">ArXiv papers, manuals, and EU documents are parsed by LLM so they can be used in a RAG pipeline</figcaption></figure></div><p>Paulien and Alena build on-premise AI systems. They created <a href="https://github.com/PaulienOut/rag-dataprep-agent">Document Preparation Agent</a>, which is RAG with extra metadata. This metadata is needed for filtering: their clients often want to add query qualifiers like &#8220;only the latest documents&#8221; or &#8220;only files written by person X&#8221;.</p><p>Because the customer data is private, the demo they used public arXiv papers, manuals, and EU documents. For v1 they focused only on PDFs only. There&#8217;s also no UI, only CLI.</p><p>Each manifest has author and document type, plus a summary, keywords, and numbered chunks.</p><p><a href="https://www.linkedin.com/in/paulien-out-023a708/">Paulien&#8217;s LinkedIn</a> and <a href="https://www.linkedin.com/in/alena-fojtik-ab439999/">Alena&#8217;s LinkedIn</a></p><h2><strong>5) Research Radar by Hana Ben Ali</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YBck!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YBck!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 424w, https://substackcdn.com/image/fetch/$s_!YBck!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 848w, https://substackcdn.com/image/fetch/$s_!YBck!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 1272w, https://substackcdn.com/image/fetch/$s_!YBck!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YBck!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png" width="1396" height="1127" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1127,&quot;width&quot;:1396,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Research Radar dashboard showing paper rating distribution and papers saved for later&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Research Radar dashboard showing paper rating distribution and papers saved for later" title="Research Radar dashboard showing paper rating distribution and papers saved for later" srcset="https://substackcdn.com/image/fetch/$s_!YBck!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 424w, https://substackcdn.com/image/fetch/$s_!YBck!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 848w, https://substackcdn.com/image/fetch/$s_!YBck!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 1272w, https://substackcdn.com/image/fetch/$s_!YBck!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d24aa92-477d-420b-9a4e-b24eb83746e9_1396x1127.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Research Radar dashboard showing star-rating distribution and papers saved for later</figcaption></figure></div><p>Hana is a structural engineer and a PhD student. She needs to stay of top of new research, but it&#8217;s very difficult: there are 100&#8211;300 new papers per day on arXiv. Scanning all the titles and abstracts manually takes hours, and relevant work is easy to miss.</p><p>Her <a href="https://github.com/55382/Research_Radar_agent_phd_assistant">Research Radar</a> solves this problem. It fetches the new papers every morning, ranks them against her interests, and emails her the best matches.</p><p>The app also builds her interest profile based on the papers she already read, and the new recommendations are based on it.</p><p>It works in two stages:</p><ol><li><p>Semantic search with cosine similarity and an author boost, keep top 15.</p></li><li><p>Re-rank with an LLM ranker to get top 5.</p></li></ol><p>The top 5 go out by email through Resend.</p><p>Stars from the email write back to <code>ground_truth_papers.csv</code>, which is both the knowledge base and the eval set.</p><p>She started from 26 papers she had rated herself. After tuning a 0.6 semantic / 0.4 LLM blend, NDCG@5 went from 0.871 to 0.923. A Streamlit dashboard shows timestamps, average score, and ratings over time. The hardest part was making the ranker get better each morning.</p><p><a href="https://www.linkedin.com/in/hana-ben-ali-357b1a18b">Hana&#8217;s LinkedIn</a></p><p>The other submissions weren&#8217;t presented live. Here they are.</p><h2><strong>6) AI Diet Coach by Thet Su Win</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JxS6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JxS6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 424w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 848w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 1272w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JxS6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI Diet Coach weekly meal plan showing Southeast Asian dishes, meal tracking, and nutrition totals&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI Diet Coach weekly meal plan showing Southeast Asian dishes, meal tracking, and nutrition totals" title="AI Diet Coach weekly meal plan showing Southeast Asian dishes, meal tracking, and nutrition totals" srcset="https://substackcdn.com/image/fetch/$s_!JxS6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 424w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 848w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 1272w, https://substackcdn.com/image/fetch/$s_!JxS6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604f074-a5b9-46f0-8842-cf757199f811_1536x864.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">AI Diet Coach weekly plan with Southeast Asian meals, tracking state, and nutrition totals</figcaption></figure></div><p><a href="https://github.com/thetsuwin66/ai-diet-coach-agent">AI Diet Coach</a> is a meal planner for Southeast Asian users. Most Western diet apps ignore this group, that&#8217;s why Thet decided to create an app.</p><p>You set goals, restrictions, cuisines, and location. The agent builds a 7-day plan from 256 recipes: 201 from TheMealDB and 55 Asian dishes added by the author. It tracks meals and weight, swaps meals, and builds a shopping list.</p><p>It also looks up USDA nutrition data and suggests nearby restaurants through Google Maps. The app is live on <a href="https://ai-diet-coach-agent-htbjcoo2pylrfhdyv2cjbm.streamlit.app/">Streamlit</a>.</p><p>A judge went through four versions on 88 labeled answers, and accuracy moved from 46.6% to 73.9%.</p><p><a href="https://sg.linkedin.com/in/thet-su-win-169221172">Thet Su Win&#8217;s LinkedIn</a></p><h2><strong>7) AI Learning OS by Wesley Tan</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8lDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8lDG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 424w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 848w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 1272w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8lDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png" width="1440" height="810" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:810,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI Learning OS queue showing staged learning resources and knowledge-base progress counts&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI Learning OS queue showing staged learning resources and knowledge-base progress counts" title="AI Learning OS queue showing staged learning resources and knowledge-base progress counts" srcset="https://substackcdn.com/image/fetch/$s_!8lDG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 424w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 848w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 1272w, https://substackcdn.com/image/fetch/$s_!8lDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100ac2ca-028d-4c5f-b9c8-1868efc266ad_1440x810.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">AI Learning OS queue of staged resources, with knowledge-base and progress counts in the sidebar</figcaption></figure></div><p>Wesley wanted to solve the watch-later graveyard problem. Self-learners save dozens of videos and articles across YouTube playlists, browser bookmarks, and note-taking apps, but most of this material is never used.</p><p>They do finish some content, but it rarely becomes structured knowledge that&#8217;s possible to use later.</p><p>To solve that, Wesley created <a href="https://github.com/wesleytanjiale/ai-learning-os">AI Learning OS</a>.</p><p>It separates saved content from learned content. You queue YouTube videos, articles, markdown, and images. After a learning walkthrough, the system writes only what you actually studied into a knowledge base. Chat search cites that base and reports progress.</p><p>The agent has six tools</p><ul><li><p>Add to queue</p></li><li><p>Browse queue</p></li><li><p>Load for learning</p></li><li><p>Consolidate to the knowledge base</p></li><li><p>Search</p></li><li><p>Get progress</p></li></ul><p>Retrieval is hybrid BM25 plus dense vectors with reciprocal rank fusion. A 400-word chunk size with 5 results scored 15/15 on the tuning set. 600-word chunks with 7 results scored 12/15. An empty-knowledge-base test checks that the agent doesn&#8217;t invent content.</p><p><a href="https://sg.linkedin.com/in/wesley-tan-jia-le-b31871219">Wesley&#8217;s LinkedIn</a></p><h2><strong>8) Applied ML Teaching Copilot by Marco Teran</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zLgB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zLgB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 424w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 848w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 1272w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zLgB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png" width="1140" height="650" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:650,&quot;width&quot;:1140,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Applied ML Teaching Copilot answer citing course material aml-001 and showing the tool-call panel&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Applied ML Teaching Copilot answer citing course material aml-001 and showing the tool-call panel" title="Applied ML Teaching Copilot answer citing course material aml-001 and showing the tool-call panel" srcset="https://substackcdn.com/image/fetch/$s_!zLgB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 424w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 848w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 1272w, https://substackcdn.com/image/fetch/$s_!zLgB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca583c6f-31ea-4fda-93e1-8f8c63976eb6_1140x650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Applied ML Teaching Copilot answers questions based on Applied ML course materials</figcaption></figure></div><p>Marco&#8217;s <a href="https://github.com/marcoteran/applied-ml-teaching-copilot">Applied ML Teaching Copilot</a> answers questions based on the Applied ML course materials. You can search, fetch a record by id, and get citations.</p><p>The stack is Streamlit, OpenAI, minsearch, PydanticAI, and Logfire. There&#8217;s a <a href="https://applied-ml-teaching-copilot.streamlit.app/">Streamlit demo</a>.</p><p>The first judge scored 0.727 accuracy. The calibrated judge reached 1.0 on 33 labeled cases.</p><p><a href="https://www.linkedin.com/in/marcoteran">Marco&#8217;s LinkedIn</a></p><h2><strong>9) Datawarehouse Agent by Lars van Asseldonk</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5dqt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5dqt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 424w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 848w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 1272w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5dqt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png" width="1440" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Datawarehouse Agent answer showing an incident count, success flags, and the read-only SQL query used&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Datawarehouse Agent answer showing an incident count, success flags, and the read-only SQL query used" title="Datawarehouse Agent answer showing an incident count, success flags, and the read-only SQL query used" srcset="https://substackcdn.com/image/fetch/$s_!5dqt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 424w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 848w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 1272w, https://substackcdn.com/image/fetch/$s_!5dqt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f188dc-b070-4be6-9843-144a9ff2d1ec_1440x875.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Datawarehouse Agent answer with success flags and the read-only SQL used for the incident count</figcaption></figure></div><p>Lars&#8217;s <a href="https://github.com/larsvasseldonk/datawarehouse_agent">Datawarehouse Agent</a> is a natural-language-to-SQL assistant over Dutch Railways (NS) station-safety incidents. Non-technical staff ask a question and get a plain-language answer plus the SQL.</p><p>Lars used PydanticAI, OpenAI, DuckDB, and Streamlit. He also used Logfire, minsearch, and Plotly, and he recorded a <a href="https://github.com/user-attachments/assets/9534b98f-0aab-442b-97e7-491813ba3e81">demo video</a>.</p><h2><strong>10) WorkerChronicle by Miki Foster</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o831!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o831!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 424w, https://substackcdn.com/image/fetch/$s_!o831!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 848w, https://substackcdn.com/image/fetch/$s_!o831!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 1272w, https://substackcdn.com/image/fetch/$s_!o831!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o831!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png" width="1456" height="860" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:860,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;WorkerChronicle project overview describing its worker's experience library and cover-letter generator&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="WorkerChronicle project overview describing its worker's experience library and cover-letter generator" title="WorkerChronicle project overview describing its worker's experience library and cover-letter generator" srcset="https://substackcdn.com/image/fetch/$s_!o831!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 424w, https://substackcdn.com/image/fetch/$s_!o831!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 848w, https://substackcdn.com/image/fetch/$s_!o831!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 1272w, https://substackcdn.com/image/fetch/$s_!o831!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38128697-e9b0-4746-aee5-ebc097921be2_1904x1124.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">WorkerChronicle&#8217;s experience library and cover-letter generator</figcaption></figure></div><p>When applying for jobs, people often struggle to remember concrete examples from their past work and adapt them to each role. Miki built <a href="https://github.com/MikiYamFos/work-chronicle">WorkerChronicle</a> to help with this.</p><p>You use can use it when applying for jobs:</p><ul><li><p>Before applying, you import your resume, previous cover letters, and raw notes to build a library of projects and accomplishments.</p></li><li><p>When you find a job, you give it the job description. The app finds relevant examples from your library, shows where evidence is missing, and creates an editable cover-letter outline.</p></li><li><p>Before an interview, it uses the same material to prepare likely questions and remind you which examples to discuss.</p></li></ul><p><a href="https://www.linkedin.com/in/mikifoster">Miki&#8217;s LinkedIn</a></p><h2><strong>11) Multi-Agent Software Development Platform by Nitesh Mishra</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OUF-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OUF-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 424w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 848w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OUF-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png" width="1400" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Multi-Agent Software Builder showing PM agent user stories, acceptance criteria, and a review step&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Multi-Agent Software Builder showing PM agent user stories, acceptance criteria, and a review step" title="Multi-Agent Software Builder showing PM agent user stories, acceptance criteria, and a review step" srcset="https://substackcdn.com/image/fetch/$s_!OUF-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 424w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 848w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!OUF-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f76a323-3877-4649-8b16-1a5e8896a1d8_1400x1000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Multi-Agent Software Builder after the PM stage, with user stories and acceptance criteria awaiting review</figcaption></figure></div><p>Nitesh&#8217;s <a href="https://github.com/mishranitesh/AI_Engineering_Buildcamp_From_RAG_to_Agents/tree/main/capstone/multi-agent-dev-platform">Multi-Agent Software Development Platform</a> turns a plain-English requirement into user stories, architecture, and backend code.</p><p>It also writes tests, reviews the generated code, and packages the result. Optionally, it can create a JIRA epic with stories and tasks, or open a draft GitHub pull request.</p><p>Each requirement goes through the same steps:</p><ul><li><p>PM agent creates user stories and acceptance criteria.</p></li><li><p>Human confirmation approves the plan before code generation.</p></li><li><p>Architect agent designs the system and creates a Mermaid diagram.</p></li><li><p>Developer agent uses RAG to generate the backend code.</p></li><li><p>QA agent writes the tests.</p></li><li><p>Review agent uses RAG to find problems in the code.</p></li><li><p>Artifact generation packages the code, tests, architecture, and README into a ZIP file.</p></li></ul><p><a href="https://www.linkedin.com/in/mishra-nitesh">Nitesh&#8217;s LinkedIn</a></p><h2><strong>12) LearnMate AI by Dianne Bronola</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hxmN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hxmN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 424w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 848w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 1272w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hxmN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png" width="1456" height="1166" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1166,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;LearnMate active review session showing a question, a student answer, and evaluated feedback&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="LearnMate active review session showing a question, a student answer, and evaluated feedback" title="LearnMate active review session showing a question, a student answer, and evaluated feedback" srcset="https://substackcdn.com/image/fetch/$s_!hxmN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 424w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 848w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 1272w, https://substackcdn.com/image/fetch/$s_!hxmN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8932363a-69d1-461e-adc0-334b365cf8e5_1504x1204.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">LearnMate active review session with a generated question, student answer, and evaluated feedback</figcaption></figure></div><p>Dianne built <a href="https://github.com/dinobronx/learnmate-ai">LearnMate AI</a> to help her learn better from YouTube videos.</p><p>Many of us have this problem: you watch a tutorial, understand it at the time, and forget most of it a week later. You can rewatch the video to refresh that knowledge, but it&#8217;s slow and ineffective. LearnMate helps with that problem.</p><p>You give it a video, it extracts concepts from it, connects them into a knowledge map. When the map is ready, it asks you questions about each concept, gives hints when you get stuck, and generates coding exercises if you need.</p><p>The stack includes FastAPI, React, PydanticAI, SQLite, ChromaDB, and OpenAI.</p><p></p><h2><strong>13) GapFinder by Katja Weber</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q2Of!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q2Of!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 424w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 848w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 1272w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q2Of!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:785,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;GapFinder report separating understood concepts from missed concepts and listing video timestamps to revisit&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="GapFinder report separating understood concepts from missed concepts and listing video timestamps to revisit" title="GapFinder report separating understood concepts from missed concepts and listing video timestamps to revisit" srcset="https://substackcdn.com/image/fetch/$s_!Q2Of!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 424w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 848w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 1272w, https://substackcdn.com/image/fetch/$s_!Q2Of!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49f8bb0c-083a-49fe-8ee2-c1647b53434d_1800x970.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">GapFinder report separating understood concepts from missed concepts and listing timestamps to revisit</figcaption></figure></div><p>Katja&#8217;s <a href="https://github.com/katjaweb/gapfinder">GapFinder</a> is another tool to help with studying. You give it a link to a YouTube tutorial, it fetches the transcript, chunks it, and asks diagnostic questions.</p><p>Then it grades your answers against the video and reports:</p><ul><li><p>what you understood vs what you missed</p></li><li><p>which sections you should rewatch</p></li></ul><p>The stack is PydanticAI, OpenAI, Streamlit, Logfire, and minsearch.</p><p><a href="https://www.linkedin.com/in/katjaweber/">Katja&#8217;s LinkedIn</a></p><p></p><h2><strong>New Cohort</strong></h2><p>I&#8217;m happy to see all these projects. At the same time, I want more participants to make it to the finish line and graduate with a completed project.</p><p>This is one of my main priorities for the next cohort. I&#8217;m actively researching what helps learners stay on track and finish with ambitious projects. I&#8217;ll apply what I learn to the course. You&#8217;ll see the results in the upcoming cohort.</p><p>The next cohort of the <a href="https://maven.com/alexey-grigorev/from-rag-to-agents">AI Engineering Bootcamp</a> starts on September 21.</p><p>If you want to build a system like these, join us. Use code &#8220;SUBSTACK&#8221; to get 20% off.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://maven.com/alexey-grigorev/from-rag-to-agents&quot;,&quot;text&quot;:&quot;AI Engineering Buildcamp&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://maven.com/alexey-grigorev/from-rag-to-agents"><span>AI Engineering Buildcamp</span></a></p><p></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[AI Dev Tools Zoomcamp 2026 Starts Today]]></title><description><![CDATA[From Idea to Production with AI Coding Agents]]></description><link>https://aishippingblog.com/p/ai-dev-tools-zoomcamp-2026-starts</link><guid isPermaLink="false">https://aishippingblog.com/p/ai-dev-tools-zoomcamp-2026-starts</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Mon, 31 Aug 2026 12:54:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4c572dda-473c-4302-8758-5142bd8782b9_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The 2026 cohort of <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a> starts today (August 31). The course is free, and registration is open for the next few weeks.</p><p>In this course, we use AI coding tools across the full software-development lifecycle.</p><ul><li><p>Part 1: <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: <a href="https://aishippingblog.com/t/ai-dev-tools-zoomcamp">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a></p></li></ul><p>In this course we give you a tool-agnostic way to work with any coding agent and increate your productivity as a developer.</p><p>Watch the launch stream:</p><div id="youtube2-YEsrF3EX4Hw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YEsrF3EX4Hw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YEsrF3EX4Hw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Start Here</strong></h2><p>Use these links to join the course and find the materials:</p><ul><li><p><a href="https://courses.datatalks.club/register/ai-dev-tools/">Register for the course</a>. Registration is free.</p></li><li><p><a href="https://courses.datatalks.club/ai-dev-tools-2026/">Open the course platform</a> to see deadlines, submit homework and projects.</p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">Star the course repository</a>. It contains the module materials, recordings, homework, and final-project requirements.</p></li><li><p><a href="https://datatalks.club/docs/courses/ai-dev-tools-zoomcamp/">Read the course documentation</a> for prerequisites, setup, logistics, and detailed guidance.</p></li><li><p><a href="https://datatalks.club/slack.html">Join the DataTalks.Club Slack</a> for talking to your peer course participants and join the <code>#course-ai-dev-tools-zoomcamp</code> channel.</p></li><li><p><a href="https://t.me/aidevtoolszoomcamp">Join the Telegram channel</a> for announcements and deadline updates.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q63d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q63d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 424w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 848w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q63d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png" width="1456" height="454" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:454,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The AI Dev Tools Zoomcamp GitHub repository shows folders for the five course modules and a description of the course&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The AI Dev Tools Zoomcamp GitHub repository shows folders for the five course modules and a description of the course" title="The AI Dev Tools Zoomcamp GitHub repository shows folders for the five course modules and a description of the course" srcset="https://substackcdn.com/image/fetch/$s_!Q63d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 424w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 848w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Q63d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a5cf474-4936-4bbd-a235-741d82635dda_1540x480.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The repository is the source for module materials, homework, recordings, and project requirements</figcaption></figure></div><p></p><h2><strong>Course Curriculum</strong></h2><p>In this course, we use AI coding tools across the full software-development lifecycle.</p><ul><li><p>Part 1: <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: <a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lWGI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lWGI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 424w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 848w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lWGI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png" width="1456" height="1543" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1543,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The curriculum page lists five modules covering AI-native workflow, full-stack development, deployment, DevOps and observability, and coding agent capabilities&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The curriculum page lists five modules covering AI-native workflow, full-stack development, deployment, DevOps and observability, and coding agent capabilities" title="The curriculum page lists five modules covering AI-native workflow, full-stack development, deployment, DevOps and observability, and coding agent capabilities" srcset="https://substackcdn.com/image/fetch/$s_!lWGI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 424w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 848w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 1272w, https://substackcdn.com/image/fetch/$s_!lWGI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd27b052d-5137-4f36-897e-9e4d8e21e64d_1536x1628.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The five modules take one application from specification through deployment, operations, and agent extensions</figcaption></figure></div><h2><strong>Module 1: AI-Native Developer Workflow</strong></h2><p>Giving a coding agent a vague request often produces a plausible application that doesn&#8217;t solve the problem you had in mind. We begin by improving the instructions and context we give it.</p><p>You turn a product idea into a specification and a backlog of small tasks. You create durable project context, then separate product management, implementation, and QA into focused sessions. You also learn how agent loops and multi-agent workflows can help with larger backlogs without removing your responsibility for review.</p><p>By the end of the module, you have a repeatable way to direct an agent and verify its work independently.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;4df66fbc-3af0-4ffb-9ce7-f6ae15bf813e&quot;,&quot;caption&quot;:&quot;This is the first article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;AI-Native Development: Specifications, Loop and Graph Engineering&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-07-22T06:59:57.781Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/youtube/w_728,c_limit/VUJxJGpaDEs&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/ai-native-development-specifications&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:208017204,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:69,&quot;comment_count&quot;:1,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p><a href="https://aishippingblog.com/p/ai-native-development-specifications">Companion article</a></p></li><li><p><a href="https://www.youtube.com/watch?v=VUJxJGpaDEs">Recording</a></p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/01-ai-native-workflow">Module 1 materials</a></p></li><li><p>Homework: <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/blob/main/cohorts/2026/homework/01-ai-native-workflow/homework.md">Instructions</a> &#183; <a href="https://courses.datatalks.club/ai-dev-tools-2026/homework/hw1">Submit</a> &#8212; due 2026-09-07</p></li></ul><h2><strong>Module 2: Build and Ship an AI-Assisted Full-Stack App</strong></h2><p>Next, you use that way of working to create a complete application. You write a product specification and build a frontend. Then you describe the API with OpenAPI, implement the backend, add SQLite persistence, and test the main behavior.</p><p>We use AI tools to produce the first version of each part, but we don&#8217;t accept generated code because it looks reasonable. We look at the code, run it, compare it with the specification, and write tests for the behavior we expect.</p><p>You finish with a tested full-stack application that runs locally and follows a documented OpenAPI specification.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;2ffdcffe-a767-46d3-9b5a-fc378f6927aa&quot;,&quot;caption&quot;:&quot;This is the second article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Build and Ship a Full-Stack App with AI Coding Assistants&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-03T12:59:20.309Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!I96k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:209595653,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:17,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p><a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Companion article</a></p></li><li><p><a href="https://www.youtube.com/watch?v=x9dq5nBpDg8">Recording</a></p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/02-development">Module 2 materials</a></p></li><li><p>Homework: <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/blob/main/cohorts/2026/homework/02-development/homework.md">Instructions</a> &#183; <a href="https://courses.datatalks.club/ai-dev-tools-2026/homework/hw2">Submit</a> &#8212; due 2026-09-14</p></li></ul><h2><strong>Module 3: Test, Containerize, and Deploy</strong></h2><p>A working local application is only the beginning. In this module, you test the seams between the frontend, backend, and database. You add integration tests, move from SQLite to Postgres, package the application with Docker, and run the checks automatically in CI.</p><p>Then you deploy the application to a public URL and connect deployment to the CI pipeline. When you merge a change that passes the required checks, the pipeline ships it.</p><p>You finish with a containerized application that other people can use and a documented way to test, deploy, and roll it back.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;c3eed8f7-e594-49fa-b7af-24df15f9b76e&quot;,&quot;caption&quot;:&quot;This is the third article in a series for AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Deploy a Full-Stack App with AI Coding Assistants&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-10T08:32:29.598Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!4sUJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:210571363,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:18,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p><a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Companion article</a></p></li><li><p><a href="https://www.youtube.com/watch?v=gxt5ZDVnBMM">Recording</a></p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/03-deployment">Module 3 materials</a></p></li><li><p>Homework: <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/blob/main/cohorts/2026/homework/03-deployment/homework.md">Instructions</a> &#183; <a href="https://courses.datatalks.club/ai-dev-tools-2026/homework/hw3">Submit</a> &#8212; due 2026-09-21</p></li></ul><h2><strong>Module 4: DevOps and Observability for AI-Built Apps</strong></h2><p>When the deployment command exits successfully, you know only that the command finished. You still need to check whether people can use the application.</p><p>You instrument an important request with OpenTelemetry and connect metrics, logs, and traces in Grafana. You write an alert for sustained user impact, collect a bounded evidence packet, and give a coding agent read-only access to that evidence. The agent can investigate and propose an action, while code outside the model enforces which actions are allowed.</p><p>You also run recurring security audits and record enough information to reconstruct an incident from the first alert to recovery.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f69db83d-059f-4d1d-b3e7-def72298536b&quot;,&quot;caption&quot;:&quot;This is the fourth article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;DevOps and Observability for an AI-Built App&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-18T11:54:47.884Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!i4Rw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/devops-and-observability-for-an-ai&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:211674506,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:14,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p><a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">Companion article</a></p></li><li><p><a href="https://www.youtube.com/watch?v=YkxLo_FRoQw">Recording</a></p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/04-devops">Module 4 materials</a></p></li><li><p>Homework: <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/blob/main/cohorts/2026/homework/04-devops/homework.md">Instructions</a> &#183; <a href="https://courses.datatalks.club/ai-dev-tools-2026/homework/hw4">Submit</a> &#8212; due 2026-09-28</p></li></ul><h2><strong>Module 5: Coding Agent Capabilities</strong></h2><p>In the final module, you look inside the coding-agent workflow and extend it around your project.</p><p>You work with project instructions, MCP, and reusable skills. You also explore hooks, specialized subagents, plugins, and small custom agents. We focus on each capability rather than one product&#8217;s name for it, so you can apply the ideas across modern coding agents.</p><p>You finish by packaging reusable instructions and a repeatable way to review changes in your application. You also include a specialist agent, an MCP tool or server, guardrails, and documented permissions.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a9a61812-ae80-4a8c-8648-a0978cae3e4a&quot;,&quot;caption&quot;:&quot;This is the fifth article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Coding Agent Building Blocks: Reusable Skills and Specialized Subagents&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-31T12:20:13.047Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LxDz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/coding-agent-building-blocks-reusable&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:213529654,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p><a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Companion article</a></p></li><li><p><a href="https://www.youtube.com/watch?v=t8OrAjNO2Zs">Recording</a></p></li><li><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/05-agent-capabilities">Module 5 materials</a></p></li><li><p>No homework</p></li></ul><p></p><h2><strong>Office Hours + AI Shipping Labs Membership</strong></h2><p>As a part of the course, we will also have office hours. During these office hours you can ask questions about the course (and beyond).</p><p>There will be 4 sessions on Tuesdays at 18:30 Berlin time (Sep 08, Sep 15, Sep 22 and Sep 29).</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/events/series/4/ai-dev-tools-office-hours&quot;,&quot;text&quot;:&quot;Join Office Hours&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/events/series/4/ai-dev-tools-office-hours"><span>Join Office Hours</span></a></p><p>Joining office hours requires a <a href="https://aishippinglabs.com/membership">paid membership on AI Shipping Labs</a>.</p><p>This course is completely free and there are no sponsors for this course, so I&#8217;m not earning anything by running it. If you want to support me, and also get more out of it, consider becoming an AI Shipping Labs member and joining office hours.</p><p>You will also get a personalized onboarding: we will discuss your current situation, your goals, and build a development plan for you.</p><p></p><h2><strong>Cohort Format</strong></h2><p>The lectures are pre-recorded, so you don&#8217;t need to attend a class at a particular time. In a live cohort, we move through the course together with shared deadlines, graded homework, and community support. You can also join the leaderboard, review peer projects, and work toward a certificate.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HWBw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HWBw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 424w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 848w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HWBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png" width="1456" height="980" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:980,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:179319,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/213541554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HWBw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 424w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 848w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!HWBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63304cc6-bc4c-48e4-b0c7-5dd12d911188_1910x1286.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Course management platform: <a href="https://courses.datatalks.club/ai-dev-tools-2026/">https://courses.datatalks.club/ai-dev-tools-2026/</a></figcaption></figure></div><p>For each module:</p><ol><li><p>Open the module folder in the course repository.</p></li><li><p>Watch the recording and follow the materials.</p></li><li><p>Complete the homework in your own public repository.</p></li><li><p>Submit your answers through the course platform before the deadline.</p></li><li><p>Ask questions and share what you learn in Slack.</p></li></ol><p>Homework is optional for the certificate, but I recommend doing it. It gives you a smaller task for each module, helps you keep pace with the cohort, and prepares you for the final project. On-time homework and learning-in-public contributions also give you leaderboard points.</p><p>You can follow all the materials without joining the live cohort. If you study at your own pace, you can watch the same recordings, complete the homework for practice, and build a portfolio project. We offer graded submissions, peer review, and certificates during the live cohort.</p><p>For more information, read the guides to <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/course-work/">course work</a>, <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/homework/">homework</a>, and <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/joining-mid-cohort/">joining a cohort after it starts</a>.</p><h2><strong>Leaderboard</strong></h2><p>The leaderboard is optional: it doesn&#8217;t affect certification. It gives you a way to track your progress alongside the cohort and recognizes contributions beyond homework. You can earn points for submitting homework on time, learning in public, contributing to the FAQ, completing projects, and reviewing peer projects.</p><p>The screenshot below shows the <a href="https://courses.datatalks.club/ai-dev-tools-2025/leaderboard">AI Dev Tools Zoomcamp 2025 leaderboard</a>. Your leaderboard profile is anonymous by default, but you can make your name, bio, and links public from your enrollment profile.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j-MV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j-MV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 424w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 848w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 1272w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j-MV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png" width="896" height="745" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:745,&quot;width&quot;:896,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The 2025 AI Dev Tools Zoomcamp leaderboard lists participant names, positions, and scores&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The 2025 AI Dev Tools Zoomcamp leaderboard lists participant names, positions, and scores" title="The 2025 AI Dev Tools Zoomcamp leaderboard lists participant names, positions, and scores" srcset="https://substackcdn.com/image/fetch/$s_!j-MV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 424w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 848w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 1272w, https://substackcdn.com/image/fetch/$s_!j-MV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34994d34-7a3c-4129-b861-e8632c326fb9_896x745.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Last year&#8217;s leaderboard recognized steady course progress and community contributions</figcaption></figure></div><p>For more information, read the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/leaderboard/">leaderboard documentation</a> for the point categories, timing, and profile options.</p><h2><strong>Learn in Public</strong></h2><p>As you work through the course, share what you build and learn. A short post about a useful idea, a problem you solved, or a screenshot of your project reinforces the material and creates a public record of your progress. It can also bring feedback, new connections, and opportunities.</p><p>After publishing a post, submit its link through the course platform with your homework, project, or peer review. Each accepted link earns a leaderboard point.</p><p>For more information, read the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/learning-in-public/">learning-in-public guide</a> for the submission process, post ideas, and examples.</p><h2><strong>FAQ and Questions</strong></h2><p>Start with the <a href="https://datatalks.club/faq/ai-dev-tools-zoomcamp.html">AI Dev Tools Zoomcamp FAQ</a> when you run into a course-specific or technical problem. It collects answers from previous cohorts, so you may find the exact error or question already covered.</p><p>If the FAQ doesn&#8217;t answer your question, ask in the <code>#course-ai-dev-tools-zoomcamp</code> Slack channel. Include what you tried, the result you expected, the result you got, and enough context for someone else to reproduce the problem. When you find a useful answer that is missing from the FAQ, send it through the <a href="https://github.com/DataTalksClub/faq/issues/new?template=faq-proposal.yml">FAQ proposal form</a>. Accepted FAQ contributions earn leaderboard points.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lDTS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lDTS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 424w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 848w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 1272w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lDTS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png" width="1456" height="694" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:694,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:164689,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/213541554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lDTS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 424w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 848w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 1272w, https://substackcdn.com/image/fetch/$s_!lDTS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15495d79-3b24-4b89-9959-1bafaac0bdd6_1942x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">FAQ available at <a href="https://datatalks.club/faq/ai-dev-tools-zoomcamp.html">https://datatalks.club/faq/ai-dev-tools-zoomcamp.html</a></figcaption></figure></div><p>For more information, read the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/asking-questions/">guide to asking questions</a> for examples and troubleshooting guidance.</p><h2><strong>Final Project and Certificate</strong></h2><p>For the <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/project">final project</a>, you choose your own idea and build a complete application with AI assistance.</p><p>Your project should include:</p><ul><li><p>a frontend and backend</p></li><li><p>an OpenAPI specification</p></li><li><p>persistent storage</p></li><li><p>automated tests</p></li><li><p>containers and a public deployment</p></li><li><p>clear instructions for running the application</p></li></ul><p>You also document how you used AI. Show how you described tasks and gave the agent context. Explain how you reviewed generated code, tested the result, and handled permissions. In the later modules, you add operations, security, and agent-extension work around the application.</p><p>Homework isn&#8217;t required for the certificate. To earn the certificate, you need to submit a passing final project during the live cohort and complete the required peer reviews on time. Check the <a href="https://courses.datatalks.club/ai-dev-tools-2026/">course platform</a> for the exact project and review deadlines.</p><ul><li><p><a href="https://courses.datatalks.club/ai-dev-tools-2026/project/project1">Project 1</a>: submit by 2026-10-19 and complete peer reviews by 2026-10-26.</p></li><li><p><a href="https://courses.datatalks.club/ai-dev-tools-2026/project/project2">Project 2</a>: submit by 2026-11-09 and complete peer reviews by 2026-11-16.</p></li></ul><p>For more information, read the <a href="https://datatalks.club/docs/courses/ai-dev-tools-zoomcamp/project/">course-specific project requirements</a>, the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/peer-review/">peer-review guide</a>, and the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/certification/">certification guide</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DCnI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DCnI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 424w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 848w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 1272w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DCnI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A 2025 AI Dev Tools Zoomcamp statement of accomplishment awarded to Rick Astley&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A 2025 AI Dev Tools Zoomcamp statement of accomplishment awarded to Rick Astley" title="A 2025 AI Dev Tools Zoomcamp statement of accomplishment awarded to Rick Astley" srcset="https://substackcdn.com/image/fetch/$s_!DCnI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 424w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 848w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 1272w, https://substackcdn.com/image/fetch/$s_!DCnI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f3fed7-64b8-40da-b6e1-4c8c1e868887_2000x1125.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Participants who complete a passing project and the required peer reviews earn a certificate</figcaption></figure></div><p></p><h2><strong>Course Links</strong></h2><p>You don&#8217;t need to remember every link, but it helps to know where to go for each part of the course.</p><ul><li><p>Use the <a href="https://courses.datatalks.club/ai-dev-tools-2026/">course platform</a> for the schedule, deadlines, submission forms, scores, project, and certificate.</p></li><li><p>Use the <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">GitHub repository</a> for module materials, recordings, code, homework, and project requirements.</p></li><li><p>Use the <a href="https://datatalks.club/docs/courses/ai-dev-tools-zoomcamp/">course docs</a> for setup and detailed guidance, and the <a href="https://datatalks.club/docs/courses/zoomcamp-logistics/">Zoomcamp logistics docs</a> for the shared rules.</p></li><li><p>Use the <a href="https://app.slack.com/client/T01ATQK62F8/C09HWT76L95">course Slack channel</a> for questions and discussion.</p></li><li><p>Use <a href="https://t.me/aidevtoolszoomcamp">Telegram</a> for announcements.</p></li><li><p>Use the <a href="https://www.youtube.com/playlist?list=PL3MmuxUbc_hLuyafXPyhTdbF4s_uNhc43">YouTube playlist</a> for recordings.</p></li></ul><h2><strong>Start the Course</strong></h2><p>Open the <a href="https://courses.datatalks.club/ai-dev-tools-2026/">course platform</a>, star the <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">course repository</a>, and join <a href="https://datatalks.club/slack.html">Slack</a> and <a href="https://t.me/aidevtoolszoomcamp">Telegram</a>. Then begin with <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/01-ai-native-workflow">Module 1</a>.</p><p>You can still join after August 31. Check the platform for the deadlines that remain, and start working through the materials.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Coding Agent Building Blocks: Reusable Skills and Specialized Subagents]]></title><description><![CDATA[This is the fifth article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.]]></description><link>https://aishippingblog.com/p/coding-agent-building-blocks-reusable</link><guid isPermaLink="false">https://aishippingblog.com/p/coding-agent-building-blocks-reusable</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Mon, 31 Aug 2026 12:20:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LxDz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the fifth article in a series based on <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>, the free course we run at DataTalks.Club.</p><p>All articles in the series:</p><ul><li><p>Part 1: <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: <a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a> (this article)</p></li></ul><p>In this article, I want to cover the two most important aspects of working with coding assistants: skills and subagents.</p><ul><li><p>A skill describes how to perform a repeatable task.</p></li><li><p>A subagent is an agent that runs in a separate context with clear task instructions.</p></li></ul><p>In addition, I&#8217;ll cover my process for creating skills and show how to run multiple agents in parallel and turn your coding agent into a task orchestrator.</p><p>Getting these concepts is enough to work with coding agents productively, and you most likely won&#8217;t need anything else.</p><p>This article is loosely based on a live workshop I did for the course. In the workshop, I had to improvise, so the content doesn&#8217;t directly map to this article. But it&#8217;s still going to be useful, so check it out.</p><div id="youtube2-t8OrAjNO2Zs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;t8OrAjNO2Zs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/t8OrAjNO2Zs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>From Markdown Files to Skills and Subagents</strong></h2><p>In Part 1 of the course, <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a>, we touched on both skills and subagents, but I didn&#8217;t define them explicitly.</p><p>Specifically, we created a few documents:</p><ul><li><p><code>_docs/process.md</code> and the other project documents contained reusable instructions (see <a href="https://github.com/alexeygrigorev/retroloop/tree/main/_docs">here</a>)</p></li><li><p><code>_docs/team/pm.md</code>, <code>_docs/team/software-engineer.md</code>, and <code>_docs/team/qa-engineer.md</code> described specialized roles for subagents (see <a href="https://github.com/alexeygrigorev/retroloop/tree/main/_docs/team">here</a>).</p></li></ul><p>I wanted to keep the discussion high-level and not focus on the mechanics of any specific implementation.</p><p>We didn&#8217;t explicitly define them because we can tell a coding assistant:</p><pre><code><code>Implement feature #101 following _docs/process.md</code></code></pre><p>It will read the document and follow the steps there.</p><p>Or you can also specify the role of the agent at the beginning of a session:</p><pre><code><code>You're a QA engineer. Your role is defined in `_docs/team/qa-engineer.md`. Review work for task #101.</code></code></pre><p>Even better, you can ask it to start a subagent with this role:</p><pre><code><code>Start a QA engineer subagent. The role is defined in `_docs/team/qa-engineer.md`. Review work for task #101.</code></code></pre><p>It will work. The coding assistant can just go read the file and do the task.</p><p>But if you define these things as skills and subagents explicitly, you&#8217;ll be more effective in using them. In this article, I&#8217;ll show you how.</p><p>Let&#8217;s start with skills.</p><h2><strong>Building Block 1: Skills</strong></h2><p>A skill is a structured set of instructions for a repeatable procedure.</p><p>If you need to follow a specific sequence of actions for completing a task, you can document the steps in a skill. Then later you can ask the agent to use this skill to perform a task. It will read the file and follow the steps from there.</p><h3><strong>My Skills</strong></h3><p>I use skills a lot.</p><p>For example, one of my skills is for releasing a new version of a Python package. I maintain a lot of Python libraries that I need to regularly update and publish. I describe how I do it in <a href="https://aishippingblog.com/p/my-pypi-release-pipeline-for-python">My PyPI Release Pipeline for Python Libraries</a>.</p><p>It&#8217;s a process with multiple steps:</p><ul><li><p>Run the tests</p></li><li><p>Update the version in pyproject.toml</p></li><li><p>Push the code to GitHub as a tag</p></li><li><p>Verify that CI publishes the package</p></li></ul><p>If I want to release a new version of <a href="https://aishippingblog.com/p/minsearch-the-small-search-library">minsearch</a>, I don&#8217;t want to describe these steps each time I do it. Instead, I document this in a <a href="https://github.com/alexeygrigorev/.agents/blob/main/skills/release/SKILL.md">release skill</a> and just say:</p><pre><code><code>release a new version</code></code></pre><p>The agent understands what I need, reads the skill, and follows the steps there.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SeJc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SeJc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 424w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 848w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 1272w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SeJc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png" width="1456" height="370" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:370,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A request to release minsearch prompts the coding agent to load the release skill and prepare version 0.2.0&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A request to release minsearch prompts the coding agent to load the release skill and prepare version 0.2.0" title="A request to release minsearch prompts the coding agent to load the release skill and prepare version 0.2.0" srcset="https://substackcdn.com/image/fetch/$s_!SeJc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 424w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 848w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 1272w, https://substackcdn.com/image/fetch/$s_!SeJc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F209fb2c7-cfa3-49fa-bcc9-69b5115ac01e_1851x470.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The agent recognizes the release request and loads the relevant skill without an explicit skill command</span></p><p>Internally, a skill is a markdown file that looks like this:</p><pre><code><span>---</span>
<span>name</span>: <span>release</span>
<span>description</span>: <span>Release a new version of the current project</span>
<span>---</span>

Release

<span>1.</span> Inspect the project and determine its package registry.
<span>2.</span> Run the tests and other required checks.
<span>3.</span> Bump the version in the source of truth.
<span>4.</span> Commit the change and push a version tag.
<span>5.</span> Verify that the CI publishing job succeeds.</code></pre><p>The main difference between a plain markdown file that we used previously and a skill is that the skill is automatically discoverable.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DMY9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DMY9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 424w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 848w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DMY9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:266209,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/213529654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DMY9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 424w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 848w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!DMY9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef44b32b-59a5-4b3c-9281-8ee96e7fc89d_2024x1318.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">My actual <a href="https://github.com/alexeygrigorev/.agents/blob/main/skills/release/SKILL.md">release skill</a></figcaption></figure></div><p>I can say:</p><pre><code><code>Make a release. The process is described in _docs/release.md</code></code></pre><p>And that will absolutely work. The agent will read the file and follow the steps there.</p><p>However, with skills I don&#8217;t need to say which file to use. The coding agent loads the list of skills into its context at startup. When I need to release a library, it can see that there&#8217;s a skill for that, so it goes ahead and uses it.</p><p><span>The agent can discover the right skill from the request, while an explicit command remains available when you want direct control</span></p><p>A skill is a markdown document, but in order to make it discoverable, we need to follow a convention:</p><ul><li><p>It must have a frontmatter section with a name and description</p></li><li><p>It&#8217;s saved in <code>.agents/skills/&lt;NAME&gt;/SKILL.md</code></p></li></ul><p>And that&#8217;s it - the rest is up to you. You can also add other markdown documents or scripts together with the <code>SKILL.md</code> file in the same directory and describe how the agent should use them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JV7u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JV7u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 424w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 848w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 1272w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JV7u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png" width="1456" height="760" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:760,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:374431,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/213529654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JV7u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 424w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 848w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 1272w, https://substackcdn.com/image/fetch/$s_!JV7u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522ed877-f944-4e86-b344-59ddc6382eb6_2812x1468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Most coding agents support the Agent Skills convention (except Claude)</figcaption></figure></div><p>The <a href="https://agentskills.io/specification">Agent Skills specification</a> describes this structure and the format in more detail.</p><p>If you want to better understand how skills work, the best way is to build a small coding agent with skills. In the AI Shipping Labs workshop <a href="https://aishippinglabs.com/workshops/coding-agent-v2">Build a Coding Agent with Tools, Skills and PydanticAI</a> I show you how. We first learn about agents and function calls, and then see how to implement a special function for loading skills.</p><h3><strong>Global Skills</strong></h3><p>There are some skills that all your projects need. But some are only needed for local work.</p><p>Examples of global skills:</p><ul><li><p>I use <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/release">release</a> for releasing a new version of a library, <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/init-library">init-library</a> to scaffold a new library or <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/create-github-repo">create-github-repo</a> to create a GitHub repository</p></li><li><p>I also <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/fetch-youtube">fetch-youtube</a>, <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/fetch-loom">fetch-loom</a>, and <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/fetch-zoom">fetch-zoom</a>, to fetch recordings or transcripts for the agent to analyze.</p></li><li><p>For creating diagrams, I have a <a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/diagram-creator">diagram-creator</a> skill.</p></li></ul><p>I use these skills across multiple projects, so it&#8217;s better to make them global, so any agent on my computer can see and use them.</p><p>To create global skills, you need to put them in</p><ul><li><p><code>~/.codex/skills</code> for Codex</p></li><li><p><code>~/.claude/skills</code> for Claude</p></li><li><p>other coding assistants have a similar structure</p></li></ul><p>I use multiple assistants, so I want all of them to use the same skills. For that, I maintain one canonical copy of the skills in my <a href="https://github.com/alexeygrigorev/.agents">.agents repository</a>, and then create symlinks from there to <code>~/.codex/skills</code>, <code>~/.claude/skills</code> and other places.</p><h3><strong>Project-Specific Skills</strong></h3><p>Most skills are not useful globally though - they are project-specific.</p><p>For example, in my projects:</p><ul><li><p><a href="https://github.com/DataTalksClub/course-management-platform/tree/main/.agents/skills">Course Management Platform</a> has an <a href="https://github.com/DataTalksClub/course-management-platform/tree/main/.agents/skills/cmp-incident-response">incident-response</a> skill. Every time there&#8217;s an alert, it knows how to investigate and fix the problem, similar to what we covered in <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">Article 4</a>.</p></li><li><p><a href="https://github.com/DataTalksClub/faq/tree/main/.agents/skills">DataTalks.Club FAQ</a> has skills for fetching course questions from Slack and turning recurring questions into FAQ entries.</p></li></ul><p>They work in the same way as global skills, but you place them in your project root:</p><ul><li><p><code>./.agents/skills/</code> for all the assistants except Claude</p></li><li><p><code>./.claude/skills/</code> for Claude Code</p></li></ul><p>It&#8217;s annoying that Claude has its own format - just like with <code>CLAUDE.md</code>.</p><p>I solve this problem by creating a symlink from <code>.claude/skills/</code> to <code>.agents/skills/</code>.</p><p>I hope Anthropic starts listening to its users and adopts the <code>.agents</code> convention too, so I can finally delete all these symlinks.</p><h3><strong>Creating Skills</strong></h3><p>You don&#8217;t need to create these skills manually. Coding assistants know the format, so you can ask them to create these skills.</p><p>However, I don&#8217;t just start a session and say &#8220;create a skill for releasing a new version of this library&#8221;. Usually, I do the task with the agent first and see where I need to correct it.</p><p>We work together until the task is done, and then at the end I say:</p><pre><code><code>Save this as a skill. Cover the workflow we followed and keep all the corrections I made in mind, so the next agent can do the task correctly from the start.

This skill is specific to this project, so don't create a global skill.</code></code></pre><p>If I don&#8217;t include the last line, Codex will create a global skill, so I always have to add it. I didn&#8217;t observe this behavior from other agents.</p><p>Improving an existing skill works in the same way. I trigger a skill, and if I see that something requires my intervention, I correct the agent. At the end of the session, I ask to update the process in the skill, so I don&#8217;t have to do it next time.</p><h2><strong>Building Block 2: Subagents</strong></h2><p>Most coding assistants allow you to start one session within another. We call the agents created this way &#8220;subagents&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LxDz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LxDz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LxDz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A meme reads, 'Yo dawg I heard you like agents, so I put an agent inside your agents, so you can prompt while you prompt'&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A meme reads, 'Yo dawg I heard you like agents, so I put an agent inside your agents, so you can prompt while you prompt'" title="A meme reads, 'Yo dawg I heard you like agents, so I put an agent inside your agents, so you can prompt while you prompt'" srcset="https://substackcdn.com/image/fetch/$s_!LxDz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!LxDz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5507188-f9e1-4108-94b3-6ae943a75425_1536x1024.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Subagents let one coding agent prompt another agent inside its own session</span></p><h3><strong>Session Context</strong></h3><p>The context for a session is all the information the agent has about the current task and all the actions it has taken.</p><p>It includes:</p><ul><li><p>AGENTS.md file</p></li><li><p>Skills and tools definitions</p></li><li><p>All the files it read</p></li><li><p>All the code it read and wrote</p></li><li><p>All the text you sent it and all the text it output</p></li></ul><p>That&#8217;s a lot of information. As the session gets longer, the agent starts &#8220;forgetting&#8221; things from the beginning. This is called &#8220;context rot&#8221;.</p><p>That&#8217;s why I start a new focused session for each task. In the article <a href="https://aishippingblog.com/p/from-idea-to-production">From Idea to Production in 28 Prompts</a>, I show the steps you need to take your idea to production and recommend starting a new session for each step.</p><p>Also, the current context will influence the future actions of the agent. If the agent implemented a feature, you shouldn&#8217;t ask the agent to test it in the same window. The implementation history will bias the agent, and it can skip some checks. Instead, it&#8217;s better to start a fresh session and ask the agent there to check the work.</p><p></p><h3><strong>Agentic Team</strong></h3><p>This is what we did in Part 1 of the course, <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a>. We defined multiple roles for our agentic team:</p><ul><li><p>Product manager (PM) - turns raw intake into specifications</p></li><li><p>Software engineer (SWE) - implements the task based on the specifications</p></li><li><p>QA engineer (QA) - validates that the task is implemented correctly</p></li></ul><p>Each of these agents lives in a separate session. The context from SWE never gets into the QA session, so the QA engineer can test the system independently.</p><p>If I have an issue #101 that&#8217;s already groomed and I want to implement it, I can start a new session and say:</p><pre><code><code>You're a software engineer agent. Your role is described in `_docs/team/swe.md`. Work on issue #101.</code></code></pre><p>But instead, I can also launch it as a subagent:</p><pre><code><code>Launch a subagent to implement #101. Follow the process.
</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zjS4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zjS4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 424w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 848w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 1272w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zjS4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png" width="1456" height="596" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:596,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A coding assistant shows a main session with a backgrounded general-purpose subagent implementing issue 101&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A coding assistant shows a main session with a backgrounded general-purpose subagent implementing issue 101" title="A coding assistant shows a main session with a backgrounded general-purpose subagent implementing issue 101" srcset="https://substackcdn.com/image/fetch/$s_!zjS4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 424w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 848w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 1272w, https://substackcdn.com/image/fetch/$s_!zjS4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9515cd0f-3696-4b41-95f7-226afe471441_1910x782.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The main session can keep working while a subagent handles a focused task in the background</span></p><p>It will create a new agent with an empty context (plus AGENTS.md) and automatically prompt it with something like this:</p><pre><code><code>You're a software engineer. Your role is defined in `_docs/team/swe.md`.
Your task is to work on issue #101. Follow the process in `_docs/process.md`.</code></code></pre><p>In some coding assistants, you&#8217;ll also be able to look inside the subagent sessions. In others, you&#8217;ll only know that the subagent is doing something, but you won&#8217;t know what exactly.</p><p></p><h3><strong>Defining Subagents</strong></h3><p>The coding assistant can read the agent definition from a markdown document (like <code>_docs/team/swe.md</code>) and launch a subagent.</p><p>Just like with skills, you can also create a reusable and discoverable subagent definition.</p><p>For example, for the software engineer, the definition could be:</p><pre><code><span>---</span>
<span>name</span>: <span>software-engineer</span>
<span>description</span>: <span>Use this agent to execute a well-scoped coding assignment.</span>
<span>---</span>

Implement the assigned task, add or update tests, run the project checks, and
report the files changed and any remaining risks. Do not expand the scope.</code></pre><p>Or even this:</p><pre><code><span>---</span>
<span>name</span>: <span>software-engineer</span>
<span>description</span>: <span>Use this agent to execute a well-scoped coding assignment.</span>
<span>---</span>

Read your role definition from <span>_</span>docs/team/swe.md</code></pre><p>You put these definitions in:</p><ul><li><p><code>.agents/agents/&lt;NAME&gt;.md</code> for the majority of assistants</p></li><li><p><code>.claude/agents/&lt;NAME&gt;.md</code> for Claude</p></li></ul><p>You can also define subagents globally, but I never needed it. My subagents are always project-specific.</p><p>Now you can ask:</p><pre><code><code>Launch software engineer for #101</code></code></pre><p>And it will start the software engineer agent.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U88l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U88l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 424w, https://substackcdn.com/image/fetch/$s_!U88l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 848w, https://substackcdn.com/image/fetch/$s_!U88l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 1272w, https://substackcdn.com/image/fetch/$s_!U88l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U88l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png" width="1456" height="647" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:647,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A coding assistant starts a software-engineer agent to implement issue 101 in the background&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A coding assistant starts a software-engineer agent to implement issue 101 in the background" title="A coding assistant starts a software-engineer agent to implement issue 101 in the background" srcset="https://substackcdn.com/image/fetch/$s_!U88l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 424w, https://substackcdn.com/image/fetch/$s_!U88l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 848w, https://substackcdn.com/image/fetch/$s_!U88l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 1272w, https://substackcdn.com/image/fetch/$s_!U88l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19b68093-eed1-4bc4-a0f3-f10eda1123fc_1890x840.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>A subagent can take on a specialized role, such as software engineering</span></p><p>Note that for some coding agents like Claude, you&#8217;ll have to restart the session to discover the agent you just defined.</p><h3><strong>Orchestration Session</strong></h3><p>When you start using subagents, your main session becomes the orchestrator.</p><p>If we have a process specified in <code>process.md</code>:</p><ul><li><p>PM grooms the issue.</p></li><li><p>SWE implements it.</p></li><li><p>QA checks it.</p></li><li><p>If the issue passes the check, QA outputs <code>PASS</code>; otherwise, it outputs <code>FAIL</code>.</p></li><li><p>If it outputs <code>FAIL</code>, SWE needs to reimplement it until QA accepts it.</p></li></ul><p>This way, we create a graph.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RpIZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RpIZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 424w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 848w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 1272w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RpIZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png" width="1179" height="191" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/109fdaee-084a-4510-9871-334fd00265fc_1179x191.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:191,&quot;width&quot;:1179,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The PM grooms an issue, the engineer implements it, and QA either completes the task or sends a failed check back to the engineer&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The PM grooms an issue, the engineer implements it, and QA either completes the task or sends a failed check back to the engineer" title="The PM grooms an issue, the engineer implements it, and QA either completes the task or sends a failed check back to the engineer" srcset="https://substackcdn.com/image/fetch/$s_!RpIZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 424w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 848w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 1272w, https://substackcdn.com/image/fetch/$s_!RpIZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109fdaee-084a-4510-9871-334fd00265fc_1179x191.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><span>The workflow from Part 1 is a graph: PM hands the issue to engineering, QA closes passing work, and failures loop back for reimplementation</span></p><p>This sequence needs to be enforced by something. Typically, we use the main coding session for that.</p><p>If we describe the process in our <code>process.md</code> document and link it in <code>AGENTS.md</code>, then we can ask:</p><pre><code><code>Implement #101</code></code></pre><p>It will automatically run #101 through the PM -&gt; SWE -&gt; QA sequence, each step will run in its own fresh context, and the orchestrator will make sure that on <code>FAIL</code> the task goes to SWE who will fix the problems.</p><h2><strong>Parallel Execution</strong></h2><p>In the first article, I only showed how to run this flow sequentially, and we can&#8217;t run it any other way: we always have to follow the PM -&gt; SWE -&gt; QA sequence.</p><p>But we can take care of multiple independent issues in parallel.</p><p>Our main agent is already the orchestrator, so it can run multiple subagents in parallel. But we need to make sure that the agents aren&#8217;t stepping on each other&#8217;s toes.</p><p>For that, we use git worktrees:</p><ul><li><p>For each new task, create a new git worktree</p></li><li><p>The agents work independently in these worktrees</p></li><li><p>You go through the entire PM -&gt; SWE -&gt; QA cycle in that worktree</p></li><li><p>When the cycle is over and the task is done, the orchestrator merges it back into main (alternatively, you can have a conflict resolver agent)</p></li><li><p>The worktree is deleted</p></li></ul><p>The orchestrator&#8217;s role here becomes more than just launching subagents in order.</p><p>It also needs to:</p><ul><li><p>Keep track of the status of each task</p></li><li><p>Schedule the agents</p></li><li><p>Create and merge worktrees</p></li><li><p>Prevent logical conflicts by picking tasks that don&#8217;t need to work on the same files</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ljue!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ljue!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 424w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 848w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 1272w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ljue!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png" width="970" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:970,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The orchestrator assigns independent issues to as many as five isolated worktrees, then merges approved branches into main one at a time&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The orchestrator assigns independent issues to as many as five isolated worktrees, then merges approved branches into main one at a time" title="The orchestrator assigns independent issues to as many as five isolated worktrees, then merges approved branches into main one at a time" srcset="https://substackcdn.com/image/fetch/$s_!Ljue!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 424w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 848w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 1272w, https://substackcdn.com/image/fetch/$s_!Ljue!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c28769f-eb75-42d4-bb71-04f213b96f16_970x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Agents work concurrently in isolated worktrees while the orchestrator controls review and merge order</span></p><p>To set it up, I put something like this in the <code>process.md</code> file:</p><pre><code><code>Work in isolated git worktrees and run up to five agents in parallel.

You are the orchestrator. Your job is to:

- Split the backlog into independent issue tracks.
- Give each agent a separate worktree and clear file ownership.
- Keep the agent slots full while eligible work remains.
- Send completed work to a fresh reviewer.
- Return review findings to the correct implementer.
- Merge approved work into the main branch one issue at a time.
- Coordinate the work. Never implement the issues yourself.</code></code></pre><p>This is the approach I use for the majority of my projects.</p><h2><strong>Summary</strong></h2><ul><li><p>Use a skill when you repeat the same sequence of actions.</p></li><li><p>Use a subagent when a task needs a separate context or a specialized role.</p></li><li><p>Use the main session as an orchestrator when several agents need to follow a process.</p></li><li><p>Use git worktrees when independent tasks need to run in parallel.</p></li></ul><p>If you want to learn more about using AI for development see the <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a> course.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[From Idea to Production in 28 Prompts]]></title><description><![CDATA[Build, deploy and operate your app with AI coding agents]]></description><link>https://aishippingblog.com/p/from-idea-to-production</link><guid isPermaLink="false">https://aishippingblog.com/p/from-idea-to-production</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 28 Aug 2026 14:21:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Yzr9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a> is the free course we run at DataTalks.Club.</p><p>Across the series we build an application with AI coding agents from the idea to making it production-ready.</p><p>I showed how to do it in 3 stages:</p><ul><li><p><a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Stage 1: Build</a> - coming up with an idea, creating frontend and backend, and running it locally</p></li><li><p><a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Stage 2: Deploy</a> - making the application testable and deployable, and setting up CI/CD</p></li><li><p><a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">Stage 3: Operate</a> - adding dev/prod environments, collecting metrics, logs, and traces, and having AI agents react to incidents</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Yzr9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Yzr9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 424w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 848w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 1272w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Yzr9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png" width="1456" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:972637,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://aishippingblog.com/i/213149828?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Yzr9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 424w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 848w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 1272w, https://substackcdn.com/image/fetch/$s_!Yzr9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9542a620-6599-4814-b08d-233e11b6e18d_1740x904.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this article I&#8217;ll put everything together, so you can use it as a reference for developing your projects. I&#8217;ll keep it tool-agnostic: I don&#8217;t assume any specific coding agent, programming language, framework, cloud, or observability tool.</p><p>It doesn&#8217;t really matter which assistant you use. You can choose Codex, Claude Code, OpenCode, Grok Build, Cursor, OpenCode, or anything else you like.</p><p>I show a sequence of steps you can follow, one after another, to take your product from a vague idea to production.</p><p>Each step is a prompt that you can put directly into your coding assistant. In most cases, it helps to start a new session for each step: this way, only the important things are in your agent&#8217;s context so it can finish the work faster and better.</p><p>Let&#8217;s go!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Stage 1: Build</strong></h2><p>This is the first stage of our process.</p><p>In the build stage, we:</p><ul><li><p>Turn the raw idea into a concrete specification</p></li><li><p>Create frontend and backend, and connect them</p></li></ul><h3><strong>1. Specification</strong></h3><p>If you give a coding agent a vague idea, it&#8217;ll fill in the gaps with its own assumptions, and you&#8217;ll get an app you didn&#8217;t want.</p><p>It&#8217;s better to be very clear about what you want and capture this in a document called &#8220;specification&#8221;.</p><p>I usually use ChatGPT for that: I open it in dictation mode and do a brain dump with my idea, and then we iterate until it&#8217;s clear what it could look like.</p><p>You can start your discussion with your AI assistant using this prompt:</p><pre><code><code>I want to build a tool for forming study groups.

Help me set the scope for this project precisely. I want to brainstorm with you and understand how the tool should work. Give me options.

Ask me one question at a time and keep your output short.</code></code></pre><p>At the end, ask the assistant to summarize the discussion into a markdown document.</p><p>Create an empty folder for your project, and save the specs into <code>_docs/specs.md</code>.</p><p>Use this document to build the frontend.</p><p></p><h3><strong>2. Frontend</strong></h3><p>I build the frontend before the backend so I can test the idea quickly. It&#8217;s the least expensive way: you create a clickable interface and you test it with some mock data. If it looks the way you want, you can continue.</p><p>Put this into your coding assistant (or tools like Lovable, v0 or Bolt.new):</p><pre><code><code>Create a system design interview application.

[Paste the ChatGPT-generated specification here.]

Centralize every backend call in one services layer, and create a mock implementation of it so the whole app runs without a real backend.

Add tests.</code></code></pre><p>Save the frontend code in the <code>frontend/</code> directory.</p><p>After you have iterated on the frontend and like it, it&#8217;s time to define how it will interact with the backend.</p><p></p><h3><strong>3. Manual test</strong></h3><p>Come up with a test you can use to validate that your application works. This should be a sequence of actions you perform to ensure the app works the way you want.</p><p>For an app that helps you find study partners, it could be something like:</p><ol><li><p>Open the home page, sign up</p></li><li><p>Create a group for studying</p></li><li><p>Create an event there</p></li><li><p>Share the event</p></li><li><p>Sign up to the event from another browser session</p></li><li><p>Verify that the sign up is visible in the first session</p></li></ol><p>You can use AI to come up with this scenario, but remember it - and maybe save it to <code>_docs</code> too.</p><p></p><h3><strong>4. AGENTS.md</strong></h3><p>AGENTS.md is loaded into the agent&#8217;s context every time you start a new session. You can add anything that you think is important for your app. For now let&#8217;s add one line there:</p><pre><code><code>Make focused, regular commits, and explain your decisions in the commit messages so we can use them as logs</code></code></pre><p>Later you can add more things here.</p><p>If you use Claude, create <code>CLAUDE.md</code> with a single line:</p><pre><code><code>@AGENTS.md</code></code></pre><p></p><h3><strong>5. OpenAPI schema</strong></h3><p>We previously asked the assistant to centralize all backend calls in one place. Now let&#8217;s use this to create the API specification. We will use this specification to define frontend-backend interaction:</p><pre><code><code>Read the frontend's API client in frontend/

Create openapi.yaml at the repository root that defines every endpoint, method, path, request body, response body, and which endpoints need authentication.</code></code></pre><p>Now we have the specification for our backend so we can build it.</p><p></p><h3><strong>6. Choose the backend stack</strong></h3><p>Before actually building it, you need to decide which technology you want to use for this application.</p><p>Ask the coding assistant to help:</p><pre><code><code>Based on openapi.yaml, propose 2-3 backend stack options. Describe pros and cons for each. Recommend one.</code></code></pre><p>You can challenge your AI assistant and ask it some questions, or just accept the option it suggests.</p><p>At the end, save the results to <code>_docs/stack.md</code>.</p><p></p><h3><strong>7. Backend</strong></h3><p>We have selected the technology, so let&#8217;s use it to build the backend.</p><p>Just like we created the frontend with mocked backend calls, we can create the backend with a mocked database. First, focus on making sure the frontend-backend integration works, and then connect a proper database.</p><pre><code><code>Build backend in backend/ based on `_docs/stack.md` that implements the openapi.yaml spec.
Use an in-memory store and seed it with data so the frontend has something to show.
Add authentication with hashed passwords and bearer tokens for the endpoints that need it.
Write tests.</code></code></pre><p></p><h3><strong>8. Makefile</strong></h3><p>I always have problems remembering how to start the backend, how to run the frontend, install the dependencies, and do other things. For that, I usually use Makefiles, so I can just type:</p><pre><code><code>make backend</code></code></pre><p>And the backend runs. Let&#8217;s define a Makefile:</p><pre><code><code>Create a Makefile so I can easily run the application.</code></code></pre><p></p><h3><strong>9. Connect frontend and backend</strong></h3><p>We created a backend but the frontend still serves the mock data. Let&#8217;s connect them:</p><pre><code><code>Switch the frontend to use the real backend client.</code></code></pre><p>Use the test scenario from before to make sure it works.</p><p>You may do a few iterations here. Test the application and ask the agent to fix the problems you discover until it works correctly.</p><p></p><h3><strong>10. Database</strong></h3><p>We don&#8217;t have a real database yet. Let&#8217;s add it. I usually use SQLite for local development, so I&#8217;ll use it here too.</p><pre><code><code>Replace the in-memory store with a database. Use SQLite with ORM, so later we can easily switch to a different database. </code></code></pre><p>Instead of SQLite, your application may need something else, so you can chat with your coding assistant about it before you implement it.</p><p>After it&#8217;s done, use the test scenario again and iterate until it fully works.</p><p></p><h2><strong>Stage 2: Deploy</strong></h2><p>Our app works locally. Great.</p><p>Let&#8217;s deploy it and make it available for others to use:</p><ul><li><p>Containerize it</p></li><li><p>Deploy it to the cloud</p></li><li><p>Automate the deployment with CI/CD</p></li></ul><p></p><h3><strong>11. Containerization</strong></h3><p>We need to put our application in a container, so we can later use any container management system for deploying it.</p><p>In our local environment, we have two processes: one for the frontend, one for the backend. We need to run them separately. In practice, you usually compile the frontend into static files and serve them with the backend.</p><p>So, instead of creating separate containers for the frontend and backend, we&#8217;ll create one that serves both:</p><pre><code><code>Create a Dockerfile that builds the frontend with Node, then builds the backend. Backend should serve the frontend.</code></code></pre><p>Build and run the image, then verify the flow with the test scenario and iterate until it works.</p><p></p><h3><strong>12. Production database</strong></h3><p>In the previous stage I used SQLite. It helps with keeping the local setup lightweight, but it&#8217;s not a database we can use in production. Typically we use something like Postgres.</p><p>You can ask your agent to select the best database for your case, but it&#8217;s very likely it&#8217;ll be Postgres.</p><p>So let&#8217;s add it:</p><pre><code><code>Add Postgres support to the backend.</code></code></pre><p>Use the test scenario to make sure the app works.</p><p></p><h3><strong>13. Docker Compose</strong></h3><p>To make it easier to manage the local environment with all the services, we use Docker Compose.</p><p>Let&#8217;s ask the agent to set it up:</p><pre><code><code>Create docker-compose.yaml with two services: database and the app.</code></code></pre><p>Run it with <code>docker compose up</code> and test it.</p><p></p><h3><strong>14. Integration</strong></h3><p>It&#8217;s possible that by now you already have tests. AI coding assistants are instructed to create them. But maybe in your project there are still none or too few.</p><p>Let&#8217;s make it explicit and ask the assistant to create them.</p><p>We also want to have a specific category of tests: integration tests. These tests are used to make sure the interaction between backend and the database is correct.</p><p>You can also ask the assistant to help you with finding the right test scenarios to include in the test suite:</p><pre><code><code>Create integration tests that run against docker-compose.yaml. What scenarios should we test?</code></code></pre><p></p><h3><strong>15. End-to-end test</strong></h3><p>Integration tests verify that the backend can connect to the database.</p><p>End-to-end tests verify the entire user-backend flow: from the browser to the frontend, from the frontend to the backend, and from the backend to the database.</p><p>So far, we have been doing these tests ourselves manually. Let&#8217;s now automate it.</p><pre><code><code>Add an end-to-end test that runs against docker-compose.yaml.

Use Playwright to:

1. &lt;Describe the first step&gt;
2. &lt;Describe the second step&gt;
3. ...

Put the tests in the e2e/ folder in the repository root.</code></code></pre><p>Now we don&#8217;t need to repeat this test manually every time we make a change.</p><p></p><h3><strong>16. Deployment stack</strong></h3><p>Now the application works locally and is properly tested. It&#8217;s time to deploy it. But where? There are many options.</p><p>Let&#8217;s choose what works best for us. Ask the coding agent:</p><pre><code><code>Propose 2-3 ways to deploy the application. Compare cost, complexity and other factors.

&lt;List any other constraints that you have&gt;</code></code></pre><p>It&#8217;s also helpful to use an infrastructure as code (IaC) tool to manage your cloud resources:</p><pre><code><code>I want to use an infrastructure-as-code tool for managing my infra. What are my options?</code></code></pre><p>It&#8217;s possible that for the deployment option you chose, there are no infrastructure-as-code options. In this case, just move on.</p><p></p><h3><strong>17. Deployment</strong></h3><p>Now that we have selected the stack, let&#8217;s deploy.</p><p>Ask the agent:</p><pre><code><code>Deploy this application to &lt;Selected option&gt;.</code></code></pre><p>If applicable, add:</p><pre><code><code>use &lt;IaC tool&gt; for managing the infrastructure</code></code></pre><p></p><h3><strong>18. CI/CD pipeline</strong></h3><p>We can deploy a few times manually, but we want to automate it. It&#8217;s done with CI/CD:</p><ul><li><p>CI is continuous integration - running tests every time you push to git</p></li><li><p>CD is continuous deployment - automatically deploying when the tests pass</p></li></ul><pre><code><code>Create a CI/CD pipeline that:

- runs backend and frontend tests in parallel
- builds the Docker Compose stack and runs integration and end-to-end tests against it
- deploys the application
- validates that the deploy is successful by checking the health endpoint</code></code></pre><p>You probably use GitHub for hosting code, so it&#8217;s likely that you&#8217;ll use GitHub Actions. If you don&#8217;t like it, chat with AI to select a different CI/CD tool.</p><p>We successfully deployed our application, but we&#8217;re not done yet. We need to make it more production-ready.</p><p></p><h2><strong>Stage 3: Operate</strong></h2><p>To make it more production-ready, we need to add a few things:</p><ul><li><p>Create dev/prod environments</p></li><li><p>Capture metrics, logs, and traces</p></li><li><p>Set up alerts</p></li><li><p>Have an AI agent taking care of the alerts</p></li></ul><p>Let&#8217;s do that.</p><p></p><h3><strong>19. Second environment</strong></h3><p>Now each push brings the updates directly to the deployment. But what if it has a bug that we missed?</p><p>To avoid that, we have two environments:</p><ul><li><p>Dev: the environment for testing. We deploy here on every push</p></li><li><p>Prod: the environment that real users use. We promote a release from dev to prod only when we confirm that it works correctly</p></li></ul><p>If we use infrastructure as code, it&#8217;s trivial to set up. If not, let the agent handle it.</p><p>Either way, it&#8217;s enough to just ask the agent to do it and it&#8217;ll figure the rest out:</p><pre><code><code>Create a second, independent copy of our infrastructure. We will use the copy as production, and the existing infrastructure as a dev environment.</code></code></pre><p></p><h3><strong>20. Manual promotion workflow</strong></h3><p>Now we automatically release the code to development on every push. Let&#8217;s set manual release promotion from dev to prod:</p><pre><code><code>Create a manual GitHub Actions workflow that promotes the dev version to production.</code></code></pre><p></p><h3><strong>21. Split build/deploy</strong></h3><p>It&#8217;s possible that in your CI/CD the build and deploy happen in one step (it happened to me when I was deploying to AWS EC2).</p><p>This is an anti-pattern. We want to build the image only once, first deploy it to development, and use it for production too if it works fine.</p><p>If for you these steps are not separate, we need to split them. Ask AI if you&#8217;re not sure.</p><pre><code><code>Currently we build the image during the deploy stage.

Split it into two stages:

- Build: build the image and push it to a container registry
- Deploy: pull the image from the registry and serve it

The manual prod promotion CI/CD workflow pulls the currently deployed dev image to prod.

Tag each image using the YYYYMMDD-HHMMSS-shortsha pattern (e.g. "20260818-163457-83242da")</code></code></pre><p>Depending on where you deploy your application, you may have to use different options for the container registry. Talk to AI if you&#8217;re not sure what to select and how to do it.</p><p></p><h3><strong>22. Observability and OpenTelemetry</strong></h3><p>Our application is deployed and serving real users in production. But we don&#8217;t know how reliable it is.</p><p>We need data for that. That&#8217;s called telemetry, and we will collect metrics, logs, and traces. The industry standard is OpenTelemetry, so you&#8217;ll probably use it.</p><pre><code><code>Instrument the backend with OpenTelemetry.

Include in the telemetry:

- service name
- environment
- deployed version</code></code></pre><p>To &#8220;instrument&#8221; the backend means to include the code for capturing the telemetry in our application. But we also need to save it somewhere.</p><p></p><h3><strong>23. Choose the observability backend</strong></h3><p>There are many options for storing the telemetry and displaying the dashboards. Some are open-source but you need to self-host them. Some are fully managed, but you&#8217;ll need to pay for using them. Some of the self-hosted options give a generous free tier.</p><p>Ask the agent to help us select:</p><pre><code><code>Propose 2-3 options for storing the telemetry (metrics, logs, traces) via OTel and for displaying them on a dashboard.

&lt;If you have any preferences or restrictions add them here&gt;</code></code></pre><p></p><h3><strong>24. OTel Collector stack</strong></h3><p>Now we&#8217;ve selected the stack. If you decide to go with open-source and self-hosting, you can also deploy it locally:</p><pre><code><code>Add an OpenTelemetry Collector. Create "observability/" directory with Docker Compose for: &lt;list selected technologies&gt;. Keep this as a separate Compose project from the application stack.</code></code></pre><p>Then deploy it:</p><pre><code><code>Deploy the observability stack. It should be separate from the application stack. Connect both development and production to it.</code></code></pre><p>If you decide to go with a managed service, connect it:</p><pre><code><code>Add an OpenTelemetry Collector for &lt;selected technology&gt;. Help me configure it</code></code></pre><p></p><h3><strong>25. Application metrics</strong></h3><p>We configured OTel. Let&#8217;s collect important metrics.</p><p>They are application-dependent and depend on what&#8217;s important for your product.</p><p>You can also ask the agent to come up with the most important metrics:</p><pre><code><code>What are the most important product metrics I need to track in my application? Give me top 5</code></code></pre><p>Start tracking them:</p><pre><code><code>Track the following metrics with OTel

- &lt;List them&gt;</code></code></pre><p></p><h3><strong>26. Dashboards</strong></h3><p>We&#8217;re tracking the metrics but not displaying them yet.</p><p>Let&#8217;s fix that:</p><pre><code><code>Display the metrics we export in a dashboard. Make it possible to filter by environment and deployed version.</code></code></pre><p></p><h3><strong>27. Alerts</strong></h3><p>We can&#8217;t babysit the metrics in a dashboard. Instead, we can configure an alert that will fire every time a metric goes too low or too high.</p><pre><code><code>Add an actionable alert for &lt;most important metric&gt;. Include the service, environment, deployed version, owner, and dashboard URL in the alert.</code></code></pre><p>It&#8217;s also useful to track 5xx error codes and for each error send an alert (or configure a threshold that makes sense for your application). Talk to your AI assistant to set it up.</p><p></p><h3><strong>28. On-call worker</strong></h3><p>Now when alerts fire, we get an email or a Slack message. But we still need to act on them.</p><p>We can have AI watch the alerts and fix the errors when they happen.</p><p>Let&#8217;s configure it.</p><pre><code><code>Add an on-call-engineer/ directory with a script that connects to our alerts API.

When an alert fires, the script

- wakes up an on-call agent that runs in headless mode
- passes all the alert details

The on-call agent should be able to resolve the issue autonomously and commit the fix.</code></code></pre><p>Your on-call engineer may have instructions that look like this:</p><pre><code><code>You are the on-call engineer for this repository. An alert just fired.

Investigate the root cause. Read the code and reproduce the failure. If you find a real bug, make the smallest correction, run the backend tests, and commit the fix with a clear message.
If the alert is a false positive, explain why and do not change the code.</code></code></pre><p></p><h2><strong>Stage 4: Going Further</strong></h2><p>In this article, I gave you 28 concrete steps to bring your idea to production. If you follow them, you&#8217;ll have a working application that can serve real users in less than a week.</p><p>But there are still many things you need to think about:</p><ul><li><p>Do you use a container manager? If not, consider using one.</p></li><li><p>Do you use a managed database service? If not, consider using one.</p></li><li><p>Regularly back up your database and test that the backups actually work.</p></li><li><p>Learn about VPCs and see if you need one.</p></li><li><p>When you start getting traffic, learn about scaling and load balancing.</p></li></ul><p>Also, periodically ask Fable or GPT-5.6-Sol Max (preferably both) to audit your code for security vulnerabilities.</p><p>I&#8217;m only scratching the surface of a &#8220;real production system&#8221; in this article, but the stages and steps in this tutorial are more than enough to get started.</p><p>With this setup, you can use AI to build a product that runs reliably. The rest you can learn when you actually need it.</p><p>Have fun building!</p><p>And check our <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a> if you want to learn more about using AI to increase your development productivity.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[DevOps and Observability for an AI-Built App]]></title><description><![CDATA[Part 4 of AI Dev Tools Zoomcamp]]></description><link>https://aishippingblog.com/p/devops-and-observability-for-an-ai</link><guid isPermaLink="false">https://aishippingblog.com/p/devops-and-observability-for-an-ai</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Tue, 18 Aug 2026 11:54:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!i4Rw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the fourth article in a series based on <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>, the free course we run at DataTalks.Club.</p><p>All articles in the series:</p><ul><li><p>Part 1: <a href="https://aishippingblog.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a> (this article)</p></li><li><p>Part 5: <a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a></p></li></ul><p>In part 2, we developed an application for conducting system design interviews. In part 3, we deployed it to a cloud environment and configured CI/CD.</p><p>In this article, we&#8217;ll make this deployment production-ready:</p><ul><li><p>Separate development and production environments</p></li><li><p>Create a manual CI/CD trigger for promoting the app from development to production</p></li><li><p>Use OpenTelemetry to collect metrics, traces, and logs and create</p></li><li><p>Define application-specific metrics and create a dashboard that displays them</p></li><li><p>Create an alert when the metrics change</p></li><li><p>Use an AI agent as the on-call responder for the alerts</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!O5gB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O5gB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 424w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 848w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 1272w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O5gB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png" width="1080" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18adc488-7028-4b99-8f96-a3511706623b_1080x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user pushes a change through CI/CD to development; a release owner promotes the tested release to production, where telemetry flows through observability and alerting to an AI agent&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user pushes a change through CI/CD to development; a release owner promotes the tested release to production, where telemetry flows through observability and alerting to an AI agent" title="A user pushes a change through CI/CD to development; a release owner promotes the tested release to production, where telemetry flows through observability and alerting to an AI agent" srcset="https://substackcdn.com/image/fetch/$s_!O5gB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 424w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 848w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 1272w, https://substackcdn.com/image/fetch/$s_!O5gB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18adc488-7028-4b99-8f96-a3511706623b_1080x720.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Deploy changes to development first, promote a tested release to production, and respond to observed failures</figcaption></figure></div><p>We will continue using AWS and CloudFormation, but the principles we show in this article are tool-agnostic and will work for any environment.</p><div id="youtube2-YkxLo_FRoQw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YkxLo_FRoQw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YkxLo_FRoQw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p></p><h2><strong>Recap</strong></h2><p>We started building the <a href="https://github.com/alexeygrigorev/interview-canvas-share">Interview Canvas project</a> in <a href="https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with">Part 2: Build and Ship a Full-Stack App with AI Coding Assistants</a>:</p><ul><li><p>We brainstormed with an AI assistant to come up with the specification</p></li><li><p>Based on that we created a React-based frontend with no backend</p></li><li><p>Next, we defined the frontend-backend OpenAPI schema</p></li><li><p>Using the API contract we created the backend</p></li><li><p>We added database support with SQLite and SQLAlchemy</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lNCj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lNCj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lNCj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png" width="900" height="320" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f80a35a6-2854-4151-8437-eb949182ba6e_900x320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:320,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user interacts with the frontend, which calls the FastAPI backend backed by SQLite&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user interacts with the frontend, which calls the FastAPI backend backed by SQLite" title="A user interacts with the frontend, which calls the FastAPI backend backed by SQLite" srcset="https://substackcdn.com/image/fetch/$s_!lNCj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!lNCj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff80a35a6-2854-4151-8437-eb949182ba6e_900x320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The React frontend calls the FastAPI backend, which stores application data in SQLite</figcaption></figure></div><p>The app was running locally, and then we deployed it in <a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Part 3: Deploy a Full-Stack App with AI Coding Assistants</a>:</p><ul><li><p>We containerized the application</p></li><li><p>Then added Postgres support</p></li><li><p>To make sure the system works reliably, we created integration and end-to-end tests</p></li><li><p>We deployed it to AWS</p></li><li><p>In the end, we set up CI/CD via GitHub Actions, so every push runs the tests and deploys the app to development</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7nZv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7nZv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 424w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 848w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 1272w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7nZv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png" width="1456" height="618" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:618,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A successful CI/CD run that tests the application and deploys it to development&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A successful CI/CD run that tests the application and deploys it to development" title="A successful CI/CD run that tests the application and deploys it to development" srcset="https://substackcdn.com/image/fetch/$s_!7nZv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 424w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 848w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 1272w, https://substackcdn.com/image/fetch/$s_!7nZv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8957d80-a8ea-415c-a41a-778543f5ae22_1962x833.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">When we push code to main, it&#8217;s automatically deployed</figcaption></figure></div><p>Now we take the application that&#8217;s already deployed and make it follow production best practices.</p><h2><strong>Dev and prod environments</strong></h2><p>When we push the code, we automatically deploy the changes, and they go live immediately. It&#8217;s okay when we are just starting to work on our project. But when we have real users, we want to be more careful and check that our changes didn&#8217;t introduce any regressions.</p><p>To avoid having this problem, we usually have two copies of the same environment:</p><ul><li><p>Dev (development): the environment we use internally for checking that everything works. Typically we run the latest version of our project there, and every time we push, the changes are automatically deployed there.</p></li><li><p>Prod (production): this is what our users use. We don&#8217;t want to deploy every single change there automatically and we want to have more control over the process.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gKA7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gKA7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 424w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 848w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 1272w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gKA7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png" width="940" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:940,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A push automatically deploys to development, while a release owner manually promotes a tested release to production&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A push automatically deploys to development, while a release owner manually promotes a tested release to production" title="A push automatically deploys to development, while a release owner manually promotes a tested release to production" srcset="https://substackcdn.com/image/fetch/$s_!gKA7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 424w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 848w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 1272w, https://substackcdn.com/image/fetch/$s_!gKA7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf6c9e80-6f2a-4444-aa53-1487c214b4f8_940x440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">On push to main we automatically deploy to dev. Promoting from dev to prod happens only after a manual action.</figcaption></figure></div><p>Because we previously defined our infrastructure as code, we can reuse most of it. We will most likely need to change a few things, like the size of the machine where the application is running, but the majority of the resources will stay the same.</p><p>Let&#8217;s ask our coding assistant to create a copy of the existing environment and call it &#8220;production&#8221;:</p><pre><code><code>Create a second, independent copy of our infrastructure. We will use the copy as production, and the existing infrastructure as a dev environment.</code></code></pre><p>Once we have the prod environment, let&#8217;s update our CI/CD. We will only deploy to dev on every push. For production, we will have a manual action that will take whatever development has and promote (apply) it to production.</p><pre><code><code>Create a manual GitHub Actions workflow that promotes the dev version to production.</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kARi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kARi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 424w, https://substackcdn.com/image/fetch/$s_!kARi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 848w, https://substackcdn.com/image/fetch/$s_!kARi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 1272w, https://substackcdn.com/image/fetch/$s_!kARi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kARi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png" width="1456" height="935" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:935,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A manual production deployment workflow with a confirmation checkbox and an optional release tag&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A manual production deployment workflow with a confirmation checkbox and an optional release tag" title="A manual production deployment workflow with a confirmation checkbox and an optional release tag" srcset="https://substackcdn.com/image/fetch/$s_!kARi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 424w, https://substackcdn.com/image/fetch/$s_!kARi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 848w, https://substackcdn.com/image/fetch/$s_!kARi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 1272w, https://substackcdn.com/image/fetch/$s_!kARi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a1a0877-76f5-4283-af4b-dfa297ba411d_1997x1283.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Promotion to prod happens only after a manual approval</figcaption></figure></div><p>Now we have two environments.</p><h2><strong>Container repository</strong></h2><p>In the pipeline we have so far, the Docker image is built during the deploy stage. I deploy to EC2 by executing the build script on the machine and then running the image in Docker.</p><p>It&#8217;s an anti-pattern.</p><p>There are multiple problems with this approach:</p><ol><li><p>The deploy stage is actually two things: build and deploy. If build fails, there should be no deploy, so it&#8217;s better to have these steps separately.</p></li><li><p>When we promote the dev version to production, we have to build again. During this time, many things could have changed, so the build will not be identical to dev, and it can cause problems.</p></li></ol><p>So we split the deploy step into two separate steps:</p><ul><li><p>Build the image and upload it to a container registry</p></li><li><p>Pull this image from the registry during the deploy</p></li></ul><p>When promoting to prod, we simply pull the same image to prod.</p><p>For AWS, we can use <a href="https://aws.amazon.com/ecr/">Amazon ECR</a> as the registry. You can also push your images to Docker Hub or another container registry if you&#8217;re running outside of AWS and your cloud doesn&#8217;t have a special service for that.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!75xn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!75xn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 424w, https://substackcdn.com/image/fetch/$s_!75xn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 848w, https://substackcdn.com/image/fetch/$s_!75xn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 1272w, https://substackcdn.com/image/fetch/$s_!75xn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!75xn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png" width="1200" height="700" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:700,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user pushes a change, then CI/CD runs separate Build and Deploy steps; the version tag goes to Deploy and a manually triggered Prod release, which updates Production&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user pushes a change, then CI/CD runs separate Build and Deploy steps; the version tag goes to Deploy and a manually triggered Prod release, which updates Production" title="A user pushes a change, then CI/CD runs separate Build and Deploy steps; the version tag goes to Deploy and a manually triggered Prod release, which updates Production" srcset="https://substackcdn.com/image/fetch/$s_!75xn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 424w, https://substackcdn.com/image/fetch/$s_!75xn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 848w, https://substackcdn.com/image/fetch/$s_!75xn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 1272w, https://substackcdn.com/image/fetch/$s_!75xn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e9bde1d-77fd-4fc3-92db-fcf212c1d06c_1200x700.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">We split build and deploy into two separate stages inside CI/CD. The build step builds a docker image, tags it and uploads to the registry. The deploy step takes the tag and pushes it to dev. The production release step takes the tag currently deployed in dev and promotes it to prod.</figcaption></figure></div><p>Let&#8217;s implement that:</p><pre><code><code>Currently we build the image during the deploy stage.

Split it into two stages:

- Build: build the image and push it to a container registry (ECR)
- Deploy: pull the image from the registry and serve it

The manual prod promotion CI/CD workflow pulls the currently deployed dev image to prod.

Tag each image using the YYYYMMDD-HHMMSS-shortsha pattern (e.g. "20260818-163457-83242da")</code></code></pre><p>Now the build step produces a tagged image, and the deploy step deploys it to dev. We can test the dev application, and when we later promote it to production, we will be certain that it&#8217;s exactly the same image.</p><h2><strong>Observability</strong></h2><p>Having two environments helps us avoid accidentally pushing buggy code to production. But accidents will still happen, and we need to make sure we detect them and react as fast as possible.</p><p>For that, we need to have observability. &#8220;Observability&#8221; means collecting information about the application so we can understand its behavior. With it, when something breaks, we can quickly find the problem.</p><p>We achieve observability by adding monitoring to our applications. At the minimum, we need to collect basic performance metrics like CPU and memory utilization and requests per second (RPS).</p><p>If we see that CPU and memory utilization are growing and RPS is dropping, something may be off.</p><p>This information alone is not enough. It doesn&#8217;t explain what happened inside a request and why it&#8217;s causing errors or degraded performance. For that, we need to collect more.</p><h2><strong>OpenTelemetry</strong></h2><p><a href="https://opentelemetry.io/docs/">OpenTelemetry</a> (often abbreviated as &#8220;OTel&#8221;) is the industry standard for telemetry.</p><p>Telemetry is all the information that the application produces:</p><ul><li><p>Metrics - requests per second, response latency, and number of errors</p></li><li><p>Logs - timestamped records of individual events, like an error message or a failed database query</p></li><li><p>Traces - all the steps (called &#8220;spans&#8221;) that a single request makes through the entire application</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Yyo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Yyo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 424w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 848w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 1272w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Yyo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png" width="780" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:780,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;An application branches to three telemetry examples: a metrics time series, a timestamped log record and a trace waterfall&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An application branches to three telemetry examples: a metrics time series, a timestamped log record and a trace waterfall" title="An application branches to three telemetry examples: a metrics time series, a timestamped log record and a trace waterfall" srcset="https://substackcdn.com/image/fetch/$s_!8Yyo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 424w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 848w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 1272w, https://substackcdn.com/image/fetch/$s_!8Yyo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb499e88e-e816-4abc-9c2c-f03947d2d549_780x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Metrics give us concrete numbers, logs give details, and traces show the path of a request with each step.</figcaption></figure></div><p>OTLP is the protocol that applications use to send this telemetry.</p><p>For many popular libraries, we can start capturing telemetry with just a few lines of code. This process is called &#8220;instrumenting&#8221; - adding telemetry collection to an application. Auto-instrumentation can instrument libraries like FastAPI without changing their code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LjEv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LjEv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 424w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 848w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 1272w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LjEv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png" width="1456" height="1045" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1045,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Python code configuring OpenTelemetry resource metadata, a tracer provider and an OTLP exporter for a FastAPI service&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Python code configuring OpenTelemetry resource metadata, a tracer provider and an OTLP exporter for a FastAPI service" title="Python code configuring OpenTelemetry resource metadata, a tracer provider and an OTLP exporter for a FastAPI service" srcset="https://substackcdn.com/image/fetch/$s_!LjEv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 424w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 848w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 1272w, https://substackcdn.com/image/fetch/$s_!LjEv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c0401f-ab92-4e85-8691-3057519664d0_1742x1250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Instrumenting FastAPI is just a few lines of code</figcaption></figure></div><p>Let&#8217;s do it. Ask the coding agent:</p><pre><code><code>Instrument the backend with OpenTelemetry.

Include in the telemetry:

- service name
- environment
- deployed version</code></code></pre><p>The application now produces telemetry and exports it over OTLP. We still need to decide where to send and store it, and how to view it. Let&#8217;s do that now.</p><h2><strong>OTel Collectors</strong></h2><p>We export telemetry over OTLP, but it&#8217;s not saved anywhere. For that, we define an <a href="https://opentelemetry.io/docs/collector/">OpenTelemetry Collector</a> between the application and the storage systems.</p><p>There are many services you can use for that. Ask your AI assistant and select the option that works best for your application.</p><p>In our case, I&#8217;ll use:</p><ul><li><p>Prometheus for metrics</p></li><li><p>Loki for logs</p></li><li><p>Tempo for traces</p></li><li><p>Grafana for dashboards</p></li></ul><p>I had never used Loki or Tempo before, but when I told my coding agent that I wanted to use Prometheus and Grafana, it suggested including Loki and Tempo too.</p><p>So let&#8217;s implement it:</p><pre><code><code>Add an OpenTelemetry Collector.

Create "observability/" directory with Docker Compose for:

- OpenTelemetry Collector
- Prometheus
- Loki
- Tempo
- Grafana

Keep this as a separate Compose project from the application stack</code></code></pre><p>Now we have the infrastructure for observability, so we&#8217;re ready to build a dashboard.</p><h2><strong>Metrics</strong></h2><p>Metrics are numerical measurements collected over time. They can show a current value, such as the number of active participants, or count events during an interval, such as rooms created or errors in the last five minutes.</p><p>For System Design Canvas, useful measurements may include:</p><ul><li><p>Total number of interview rooms</p></li><li><p>Number of active rooms and number of active participants</p></li><li><p>Number of elements on the canvas across the application</p></li><li><p>Change propagation delay: when I add an element to the canvas, how long does it take for you to see it</p></li><li><p>Number of errors</p></li></ul><p>Let&#8217;s start collecting some of them:</p><pre><code><code>Track these application metrics:

- interview rooms created
- active interview participants
- canvas elements created
- failures in component creation

Include the environment and deployed version</code></code></pre><p>Now we have the data and can display it in a dashboard:</p><pre><code><code>Add a Grafana panel with these metrics. Make it possible to filter by environment and deployed version.</code></code></pre><p>Run it locally and test it by opening our application, creating a room, adding a canvas element, and checking that it appears in Grafana.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DYr-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DYr-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DYr-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A dark Grafana-style dashboard with five-minute event counts for interview rooms, canvas elements and component creation failures, plus active participants, filtered to production and a deployed version&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A dark Grafana-style dashboard with five-minute event counts for interview rooms, canvas elements and component creation failures, plus active participants, filtered to production and a deployed version" title="A dark Grafana-style dashboard with five-minute event counts for interview rooms, canvas elements and component creation failures, plus active participants, filtered to production and a deployed version" srcset="https://substackcdn.com/image/fetch/$s_!DYr-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!DYr-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091a0f56-6f8e-498b-ac5e-effc59aa0fb7_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Grafana dashboard with the metrics</figcaption></figure></div><h2><strong>Deploy the observability stack</strong></h2><p>If you use the same stack as me (Prometheus, Grafana and others), we need to deploy and manage them ourselves.</p><p>If you use a managed OTel-compatible service such as CloudWatch, Grafana Cloud, Datadog, or Sentry, you can skip this step.</p><p>So if you&#8217;re following my steps, let&#8217;s deploy our stack:</p><pre><code><code>Deploy the observability stack. It should be separate from the application stack.
Connect both development and production to it.</code></code></pre><p>After it finishes, you can open Grafana and perform the same test that we did locally.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i4Rw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i4Rw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 424w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 848w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 1272w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i4Rw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png" width="1456" height="605" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:605,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The build-once deployment workflow extended with OTel attached to development and production; a collector sends metrics to Prometheus, logs to Loki and traces to Tempo for Grafana dashboards&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The build-once deployment workflow extended with OTel attached to development and production; a collector sends metrics to Prometheus, logs to Loki and traces to Tempo for Grafana dashboards" title="The build-once deployment workflow extended with OTel attached to development and production; a collector sends metrics to Prometheus, logs to Loki and traces to Tempo for Grafana dashboards" srcset="https://substackcdn.com/image/fetch/$s_!i4Rw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 424w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 848w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 1272w, https://substackcdn.com/image/fetch/$s_!i4Rw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Development and production export telemetry through OTel. The collector sends metrics to Prometheus, logs to Loki and traces to Tempo. Grafana reads from them and displays this information on dashboards.</figcaption></figure></div><p>Note: here we deploy Grafana and the other services openly on the Internet, so everyone can access them. In practice, you put sensitive resources like observability services and databases on private networks, restrict network access, and require authentication so only authorized users can access them.</p><h2><strong>Alerting</strong></h2><p>We have the dashboards, but we can&#8217;t watch them all the time to see if some metrics drop or grow too large. If this happens, we need a system that triggers an alert saying that something happened.</p><pre><code><code>Add an actionable alert for repeated canvas component-creation failures.

Use a threshold and duration that represent real user impact. Include the
service, environment, deployed version, owner, and dashboard URL in the alert.</code></code></pre><h2><strong>On-Call Engineer</strong></h2><p>Now that we have alerts, we need to react to them. In companies, there&#8217;s usually an on-call engineer. That&#8217;s the engineer who receives the alert when something happens. They need to figure out what&#8217;s happening and find a quick solution to stop the problem.</p><p>When a metric crosses its threshold for long enough, the alert fires and the on-call engineer receives it.</p><p>Our AI assistant can be the on-call engineer, and if something happens, it can try to fix it. In real scenarios, AI on-call agents also need to understand if the issue is serious enough to escalate the alert to a human on-call engineer.</p><p>In our case, we won&#8217;t do it. We will implement a system that checks the observability system for alerts, and if something is happening, an agent session will start and try to fix the problem.</p><pre><code><code>Add an on-call-engineer/ directory with a script that polls the observability
alert API every minute.

When an alert fires, pass the alert details to a headless coding agent.</code></code></pre><p>As a result, we have a worker that polls for alerts and starts an agent session when one fires.</p><p>In my case, the agent had this prompt:</p><pre><code><code>You are the on-call engineer for this repository. An alert just fired.

Investigate the root cause. Read the code and reproduce the failure.
If you find a real bug, make the smallest correction, run the backend tests,
and commit the fix with a clear message.

If the alert is a false positive, explain why and do not change the code.</code></code></pre><p>This is a small proof-of-concept script. In reality, you will probably have a system that looks like this:</p><ul><li><p>An alert is triggered and sent via SNS or a similar service.</p></li><li><p>A Lambda reacts to that alert and starts a container job.</p></li><li><p>The job launches Codex or Claude in headless mode.</p></li><li><p>Once the session is over, the logs are saved and the compute is terminated.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JrQ5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JrQ5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 424w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 848w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 1272w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JrQ5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png" width="1080" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;An alert passes through SNS and Lambda to an isolated agent container with access to code, logs and metrics; the session log is saved after the run&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An alert passes through SNS and Lambda to an isolated agent container with access to code, logs and metrics; the session log is saved after the run" title="An alert passes through SNS and Lambda to an isolated agent container with access to code, logs and metrics; the session log is saved after the run" srcset="https://substackcdn.com/image/fetch/$s_!JrQ5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 424w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 848w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 1272w, https://substackcdn.com/image/fetch/$s_!JrQ5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa95f1d0-e94a-40e3-a8c0-c981612b68a8_1080x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The alert starts an agent running inside a container job. When the job is done, the agent saves the log and the machine is teminated.</figcaption></figure></div><p>Let&#8217;s test our idea by introducing a bug.</p><h2><strong>Introducing a bug</strong></h2><p>We want to test that this system works, so let&#8217;s start a coding agent and ask it to add a bug or find an existing one.</p><pre><code><code>Introduce a realistic bug in canvas component creation.

For some requests, creating a component should fail even though the existing tests pass. Keep the failure reproducible so we can test that the bug causes an alert and the on-call response.</code></code></pre><p>Here, the goal is to debug the process and make sure our on-call engineer actually wakes up and solves the problem.</p><p>After a few iterations, we have an on-call engineer that wakes up when there&#8217;s an alert and tries to fix the problem.</p><h2><strong>Clean up</strong></h2><p>After you finish everything for the module, I recommend cleaning up everything. We created these things for learning, so we shouldn&#8217;t forget to clean them up. If you don&#8217;t, expect a bigger bill at the end of the month.</p><p>Ask your agent to clean the infra:</p><pre><code><code>Delete the CloudFormation stacks.</code></code></pre><p>Check yourself that everything is indeed gone. You may want to double-check everything in the AWS console or ask another agent to scan the running resources in your account.</p><h2><strong>Next steps after this module</strong></h2><p>We started with an idea, and now our setup is much closer to production. But here I could only briefly touch on the most important concepts.</p><p>There are many more things that you should consider for a real production application:</p><ul><li><p>Here I deploy to EC2 by running a shell script on the instance. It&#8217;s okay for a proof of concept but very problematic for production. Use a container management system like ECS or an alternative.</p></li><li><p>Sometimes you will need to roll back a change you promoted to prod. Ask your agent to make it easy to do.</p></li><li><p>Use a managed database service.</p></li><li><p>Regularly back up your database. It&#8217;s best if your backups live outside of your infrastructure-as-code stack and you have multiple independent copies.</p></li><li><p>Test that you can actually use these backups.</p></li><li><p>Put internal services and resources on private subnets inside a VPC, and restrict network access to them.</p></li><li><p>If your application needs to handle a lot of traffic, learn about scaling and load balancing. Container management systems make this easier to manage.</p></li><li><p>Ask Fable or GPT-5.6-Sol Max (better both) to audit your code for security vulnerabilities. Do it multiple times.</p></li></ul><h2><strong>Next in the series</strong></h2><p>With this article, we finish developing our end-to-end application. We took it from a raw idea and turned it into a system that&#8217;s much closer to production.</p><p>In the last article, we will look into coding agent capabilities:</p><ul><li><p>MCP</p></li><li><p>skills</p></li><li><p>commands</p></li><li><p>plugins</p></li><li><p>specialized agents</p></li></ul><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>You can find the course materials and the next cohort in <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>. It&#8217;s free.</p>]]></content:encoded></item><item><title><![CDATA[How to Do Evals in 2026]]></title><description><![CDATA[A tool-agnostic framework for evaluating AI agents]]></description><link>https://aishippingblog.com/p/how-to-do-evals-in-2026</link><guid isPermaLink="false">https://aishippingblog.com/p/how-to-do-evals-in-2026</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 14 Aug 2026 18:47:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bCza!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Across all 4,894 AI engineering job descriptions that I collected for the <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineering Field Guide</a>, evaluation consistently comes as the number one skill that AI Engineers must have.</p><p>It also appears in interview questions. In interviews, you&#8217;ll definitely hear something like &#8220;how do you evaluate a RAG pipeline?&#8221; or &#8220;how do you measure agent performance?&#8221;. If you don&#8217;t include evals in your home assignment, it&#8217;s a red flag.</p><p>Why is it such an important skill? Because it&#8217;s hard, and it matters.</p><p>It&#8217;s easy to build an agent these days. I show how to do it in my <a href="https://aishippinglabs.com/courses/aihero">&#8220;AI Hero: 7-Day AI Agents Crash-Course&#8221;</a>. You need to get an OpenAI key, take any agentic framework, define the instructions and the tools, and that&#8217;s it. You have an agent.</p><p>But this agent will break in so many ways. It will:</p><ul><li><p>Give a wrong answer</p></li><li><p>Confidently hallucinate a plausible answer</p></li><li><p>Finish without giving any answer</p></li><li><p>Get into a loop and call the same tool over and over again</p></li><li><p>Exhaust the context</p></li><li><p>Call a tool with invalid arguments</p></li></ul><p>That&#8217;s just the tip of the iceberg. To make sure this agent works reliably, you need to test and evaluate it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bCza!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bCza!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 424w, https://substackcdn.com/image/fetch/$s_!bCza!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 848w, https://substackcdn.com/image/fetch/$s_!bCza!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 1272w, https://substackcdn.com/image/fetch/$s_!bCza!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bCza!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png" width="642" height="380.8971820258949" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:779,&quot;width&quot;:1313,&quot;resizeWidth&quot;:642,&quot;bytes&quot;:976390,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/211217209?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bCza!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 424w, https://substackcdn.com/image/fetch/$s_!bCza!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 848w, https://substackcdn.com/image/fetch/$s_!bCza!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 1272w, https://substackcdn.com/image/fetch/$s_!bCza!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd560ed5d-f374-48db-b81d-8d3f487ec359_1313x779.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Also, without evaluations, you&#8217;re blind. You don&#8217;t know how the agent is performing. You also can&#8217;t really change anything: every change you introduce may break the system in many ways, including the areas where you least expect it.</p><p>Agent evaluation is not simple. It takes a lot of time to understand the problem, get input from real users, and gather the right data. It&#8217;s not something you can just delegate to Claude and forget about it.</p><p>That&#8217;s what makes evals such an important part of AI Engineering.</p><p>In this article, I will tell you about my approach to evaluations:</p><ul><li><p>Start manually by &#8220;vibe-checking&#8221; the system, but also collect logs as early as possible</p></li><li><p>Create a tool for labelling the logs and put together a gold standard dataset</p></li><li><p>Create a judge that&#8217;s aligned with our judgement</p></li><li><p>Break the agent like a QA engineer</p></li><li><p>Get more data by generating it synthetically</p></li><li><p>Start collecting data from real users</p></li><li><p>Monitor your system with online evaluation</p></li><li><p>Always refine your gold standard dataset</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RDZh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RDZh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 424w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 848w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 1272w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RDZh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png" width="1456" height="747" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:747,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A descending staircase of six stages - vibe-check and log, label good or bad, align the judge, break the agent, synthetic data, real users&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A descending staircase of six stages - vibe-check and log, label good or bad, align the judge, break the agent, synthetic data, real users" title="A descending staircase of six stages - vibe-check and log, label good or bad, align the judge, break the agent, synthetic data, real users" srcset="https://substackcdn.com/image/fetch/$s_!RDZh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 424w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 848w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 1272w, https://substackcdn.com/image/fetch/$s_!RDZh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8d571b4-c691-4595-bef4-57e360e5d9c3_1560x800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The stages, in the order I go through them. Each stage adds new cases to the gold standard dataset.</figcaption></figure></div><p>Let&#8217;s get into details.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Collecting the Gold Standard Dataset</strong></h2><p>You have an agent. What&#8217;s next?</p><p>There&#8217;s nothing wrong with &#8220;vibe-checking&#8221; it: you poke it, ask questions you want it to answer, and look at the results. If they aren&#8217;t good, you figure out how to fix it.</p><p>But already at this step you can be a bit more organized and start logging what the agent is doing. When you ask a question and see the answer, make sure they are saved somewhere. You do a few vibe-checking sessions, and you already have 10-15 logged records.</p><p>Now you can systematically look at these logs and classify each record into</p><ul><li><p>&#8220;good&#8221;: the answer is what you expect</p></li><li><p>&#8220;bad&#8221;: the answer is not what you wanted to see</p></li></ul><p>To help me do it, I usually vibe-code a small labelling tool.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xRpV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xRpV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 424w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 848w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 1272w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xRpV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png" width="626" height="496.7173913043478" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1012,&quot;resizeWidth&quot;:626,&quot;bytes&quot;:945110,&quot;alt&quot;:&quot;Labelling tool showing a logged record with the user question, the assistant answer, and retrieved FAQ entries, plus Good, Bad, and Skip buttons and a progress bar&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Labelling tool showing a logged record with the user question, the assistant answer, and retrieved FAQ entries, plus Good, Bad, and Skip buttons and a progress bar" title="Labelling tool showing a logged record with the user question, the assistant answer, and retrieved FAQ entries, plus Good, Bad, and Skip buttons and a progress bar" srcset="https://substackcdn.com/image/fetch/$s_!xRpV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 424w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 848w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 1272w, https://substackcdn.com/image/fetch/$s_!xRpV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc1b87f7-e242-4c0e-8ed2-a366896d1b89_1012x803.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The labelling tool for the &#8220;version 0&#8221; of the gold standard dataset</figcaption></figure></div><p>This gives you a &#8220;version 0&#8221; of the gold standard dataset - the dataset you will use for evaluating your system (sometimes it&#8217;s also called the &#8220;ground truth&#8221; dataset).</p><p>Don&#8217;t delegate this step to coding assistants. It&#8217;s only a few examples, so you can go through them relatively quickly. But this way, you will get a lot of insights into how your agent is behaving, what&#8217;s working and what isn&#8217;t.</p><h2><strong>Judge alignment</strong></h2><p>Once you manually labelled 10-15 examples, it&#8217;s time to start automating it.</p><p>I use coding assistants for that:</p><ul><li><p>Ask the assistant to analyze the logs and classify the examples into good or bad.</p></li><li><p>See if the results match yours. If they don&#8217;t, ask the agent to explain its decision - maybe you missed something, or the agent didn&#8217;t have enough context.</p></li><li><p>Iterate until you both agree.</p></li><li><p>At the end of the session, ask the agent to create a judge.md file that describes the classification rules.</p></li><li><p>Read this file carefully to see that the rules are generic and not specific to the examples you have in the dataset.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yFJA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yFJA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 424w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 848w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 1272w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yFJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png" width="596" height="386.4175824175824" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:944,&quot;width&quot;:1456,&quot;resizeWidth&quot;:596,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Example judge.md file with GOOD and BAD criteria for an FAQ assistant, ending with the rule when uncertain, prefer BAD&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Example judge.md file with GOOD and BAD criteria for an FAQ assistant, ending with the rule when uncertain, prefer BAD" title="Example judge.md file with GOOD and BAD criteria for an FAQ assistant, ending with the rule when uncertain, prefer BAD" srcset="https://substackcdn.com/image/fetch/$s_!yFJA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 424w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 848w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 1272w, https://substackcdn.com/image/fetch/$s_!yFJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F750ce6b3-f1de-4527-a94b-d8b33d9063c7_1475x956.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">An example of a judge.md file with generic classification rules</figcaption></figure></div><p>We just created a judge - a system that evaluates our agent.</p><p>Now we need to test that using judge.md alone is sufficient:</p><ul><li><p>Start a new session</p></li><li><p>Ask to start a subagent which reads judge.md and classifies the records in your dataset</p></li><li><p>If something is off, ask the agent to change judge.md</p></li><li><p>Iterate until you both align on what&#8217;s good and bad</p></li></ul><p>This process is called &#8220;alignment&#8221; - we align the judge with what we think is good or bad.</p><p>It&#8217;s the same as training a binary classifier that predicts your decisions. But instead of weights, you have a judge.md file. (And it&#8217;s also helpful to have a train-test split here too! But later, when you have more data.)</p><p>At this point, you also get a metric - the fraction of good examples. Now if you change something in your agent, you can re-run the judge and make sure this metric doesn&#8217;t go down.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eG3Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eG3Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 424w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 848w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 1272w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eG3Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png" width="1456" height="825" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:825,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Terminal running Codex, which executed the agent against the dataset, judged 15 records with judge.md, reported 12 good and 3 bad results with an 80% score, and asks whether to inspect the bad cases&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Terminal running Codex, which executed the agent against the dataset, judged 15 records with judge.md, reported 12 good and 3 bad results with an 80% score, and asks whether to inspect the bad cases" title="Terminal running Codex, which executed the agent against the dataset, judged 15 records with judge.md, reported 12 good and 3 bad results with an 80% score, and asks whether to inspect the bad cases" srcset="https://substackcdn.com/image/fetch/$s_!eG3Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 424w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 848w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 1272w, https://substackcdn.com/image/fetch/$s_!eG3Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F539961c7-5034-4d34-af11-122c2c3245b5_1586x899.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Codex running the evaluation: 12 good, 3 bad, 80% score</figcaption></figure></div><h2><strong>Breaking the agent</strong></h2><p>We have the initial version of our gold standard dataset. But it&#8217;s not enough. Now we want to find cases that break our system.</p><p>QA engineers have been breaking software for many decades. For coming up with cases that should derail our agent, we can borrow some concepts from their frameworks.</p><h3><strong>1. Equivalence partitions</strong></h3><p>The first step is <a href="https://www.ministryoftesting.com/software-testing-glossary/equivalence-partitioning">equivalence partitioning</a>. You divide the input space into groups where inputs within each group should produce similar behavior.</p><p>If we have a RAG agent that answers questions about our docs, it could be:</p><ul><li><p>Questions about topics in the documentation,</p></li><li><p>Questions about relevant topics but not in the documentation,</p></li><li><p>Questions about irrelevant topics,</p></li><li><p>Ambiguous questions,</p></li><li><p>Questions in different languages</p></li></ul><p>Once you come up with a list of groups, come up with 2-3 questions from each group. You can ask AI to help you with the entire process.</p><h3><strong>2. Boundary testing</strong></h3><p>The next concept is <a href="https://www.ministryoftesting.com/software-testing-glossary/boundary-testing">boundary testing</a>.</p><p>We already have some questions from the previous step, but we can be even more aggressive in our testing.</p><p>After defining the partitions, QA engineers would test the boundaries of each partition. We can do the same.</p><p>For a RAG agent, come up with queries that:</p><ul><li><p>Exactly match a document that we have in our database (easy case),</p></li><li><p>Are semantically related but use different words (typical case),</p></li><li><p>Are semantically related to multiple documents (ambiguous case),</p></li><li><p>Are completely off-topic (boundary case).</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gLJk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gLJk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 424w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 848w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 1272w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gLJk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png" width="1456" height="784" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:784,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Spreadsheet with test questions for a course FAQ agent, with columns for id, course, question, kind, and expected outcome, covering kinds like in_docs, not_in_docs, ambiguous, off_topic, different_language, exact_match, paraphrase, and short_query&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Spreadsheet with test questions for a course FAQ agent, with columns for id, course, question, kind, and expected outcome, covering kinds like in_docs, not_in_docs, ambiguous, off_topic, different_language, exact_match, paraphrase, and short_query" title="Spreadsheet with test questions for a course FAQ agent, with columns for id, course, question, kind, and expected outcome, covering kinds like in_docs, not_in_docs, ambiguous, off_topic, different_language, exact_match, paraphrase, and short_query" srcset="https://substackcdn.com/image/fetch/$s_!gLJk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 424w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 848w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 1272w, https://substackcdn.com/image/fetch/$s_!gLJk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c05f23-2845-48da-98f3-25c13b4a60a3_1589x856.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Test questions from equivalence partitions and boundary testing, tagged by kind and expected outcome</figcaption></figure></div><p></p><h2><strong>Further alignment</strong></h2><p>This way you can easily add 30-40 more questions to your gold standard dataset.</p><p>Let&#8217;s take all the data we have, and run it against our agent. Then use the judge to classify the output.</p><p>We already have a tool that we created earlier for manual evaluation. At this point, I adjust this tool to also support checking the output of the judge. For each record, I just decide whether I &#8220;agree&#8221; or &#8220;disagree&#8221; with the judge. I find it more convenient than labelling the new logs as &#8220;good&#8221; or &#8220;bad&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fw8r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fw8r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 424w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 848w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 1272w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fw8r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png" width="650" height="441.9630156472262" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:956,&quot;width&quot;:1406,&quot;resizeWidth&quot;:650,&quot;bytes&quot;:1526441,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/211217209?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5f16480-b5de-45c6-8bc2-f2c821adef27_1448x1086.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Fw8r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 424w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 848w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 1272w, https://substackcdn.com/image/fetch/$s_!Fw8r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cac8a1b-426a-464e-94fb-0180247dd022_1406x956.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If we have many &#8220;disagree&#8221; cases, tune the judge following the same process as before. We will never have 100% alignment, so we don&#8217;t need to be too strict about it, but the judge should be correct most of the time.</p><h2><strong>Using the Judge to improve your agent</strong></h2><p>Now we have a fully automated process that:</p><ol><li><p>Runs the agent against our data</p></li><li><p>Runs the judge against the output of step 1</p></li><li><p>Outputs a metric - the fraction of &#8220;good&#8221; results</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3Xg2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3Xg2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 424w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 848w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 1272w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3Xg2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png" width="1310" height="370" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:370,&quot;width&quot;:1310,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Pipeline diagram: the gold standard dataset feeds the agent, the agent output goes to the judge which reads judge.md, and the judge produces a metric, with a feedback arrow going back to the agent&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Pipeline diagram: the gold standard dataset feeds the agent, the agent output goes to the judge which reads judge.md, and the judge produces a metric, with a feedback arrow going back to the agent" title="Pipeline diagram: the gold standard dataset feeds the agent, the agent output goes to the judge which reads judge.md, and the judge produces a metric, with a feedback arrow going back to the agent" srcset="https://substackcdn.com/image/fetch/$s_!3Xg2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 424w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 848w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 1272w, https://substackcdn.com/image/fetch/$s_!3Xg2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F713ee840-c63d-4d6a-9694-5ea51c7b7c84_1310x370.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The automated evaluation pipeline: fix and re-run</figcaption></figure></div><p>Because it&#8217;s automated, we can use it to improve our agent. You can ask the coding assistant to do it.</p><p>I recommend following the <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">implementer-tester pattern</a> for that:</p><ul><li><p>Define the implementer subagent that modifies the agent</p></li><li><p>Define the judge subagent that uses judge.md to evaluate the output</p></li><li><p>Iterate until your &#8220;good&#8221; ratio is acceptable</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4E8p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4E8p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 424w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 848w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 1272w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4E8p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png" width="680" height="410.52197802197804" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:879,&quot;width&quot;:1456,&quot;resizeWidth&quot;:680,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two terminal sessions side by side: the implementer agent modifying the agent code, and the tester running the evaluation and reporting the results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two terminal sessions side by side: the implementer agent modifying the agent code, and the tester running the evaluation and reporting the results" title="Two terminal sessions side by side: the implementer agent modifying the agent code, and the tester running the evaluation and reporting the results" srcset="https://substackcdn.com/image/fetch/$s_!4E8p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 424w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 848w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 1272w, https://substackcdn.com/image/fetch/$s_!4E8p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8729ec6e-cc59-4544-b194-d5d4eb8bfebb_2234x1349.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The implementer-tester loop: one session changes the agent, the other one evaluates it</figcaption></figure></div><h2><strong>Synthetic data</strong></h2><p>So far we focused on the quality of our gold standard dataset. In this section, I want to talk about the quantity.</p><p>Quantity is also important because agents are non-deterministic. Even if the agent works properly on our small dataset, it will break when we get more traffic - just because of probability and statistics. When you expose your agent to a bigger number of inputs, things will break: with more repetitions, you increase the chances of something going wrong. That&#8217;s why you want quantity here.</p><p>We can generate a lot more data using AI. Open your AI assistant, ask it to analyze your code, describe to it your target users, and then tell it to generate questions that these users are likely to ask.</p><p>For a RAG system, you can:</p><ul><li><p>Take a document from your knowledge base</p></li><li><p>Ask AI to come up with 5 questions based on this document</p></li><li><p>Do it for all documents</p></li></ul><p>You will end up with a lot of questions. You can keep all of them, or take a sample.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_gXJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_gXJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 424w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 848w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 1272w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_gXJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png" width="714" height="523.7307692307693" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1068,&quot;width&quot;:1456,&quot;resizeWidth&quot;:714,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Terminal session where an AI assistant generated five user questions from a course document, covering homework deadlines, certificates, project submission, and technical issues&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Terminal session where an AI assistant generated five user questions from a course document, covering homework deadlines, certificates, project submission, and technical issues" title="Terminal session where an AI assistant generated five user questions from a course document, covering homework deadlines, certificates, project submission, and technical issues" srcset="https://substackcdn.com/image/fetch/$s_!_gXJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 424w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 848w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 1272w, https://substackcdn.com/image/fetch/$s_!_gXJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8be12d7-0c41-421e-8f8c-8157abdf2595_1568x1150.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Generating synthetic questions from a document in the knowledge base</figcaption></figure></div><p>Then follow the same process: run your agent against this data and then use the judge to evaluate the results.</p><p>This way you will probably run into many different problems:</p><ul><li><p>The agent will loop without stopping and exhaust the context</p></li><li><p>It won&#8217;t follow the instructions and do something that it shouldn&#8217;t</p></li><li><p>A tool will break and your agent won&#8217;t finish the task</p></li></ul><p>And many others. You don&#8217;t know in advance what you&#8217;ll get, which is why you want to expose the agent to a wide variety of inputs.</p><p>Once you identify the scenarios where your agent breaks or doesn&#8217;t perform well, add them to your gold standard dataset.</p><p>Then you can fix the agent, and re-run the whole evaluation set to make sure you don&#8217;t introduce any regressions.</p><h2><strong>Start testing on real users as soon as possible</strong></h2><p>We have evaluated our agent on many cases, so we&#8217;re confident that it&#8217;s working okay. But we always need more data.</p><p>Now it&#8217;s time to put it in front of real users:</p><ul><li><p>Invite people for user testing sessions to check your system</p></li><li><p>If you plan to integrate it into an existing product, include it in a small subset of the traffic</p></li><li><p>If it&#8217;s a small personal project, ask your friends and family to test it.</p></li><li><p>If it&#8217;s a course project, ask your classmates to play with it.</p></li><li><p>Share it on social media.</p></li></ul><p>Make sure you&#8217;re collecting the logs (be direct with the user about what you&#8217;re collecting) and use this for adding new cases into the gold standard dataset.</p><p>Add +1/-1 buttons. This way, the users can give you explicit feedback, and if somebody dislikes the answer, you should inspect it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5B7m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5B7m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 424w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 848w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 1272w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5B7m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png" width="1448" height="945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:945,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1431256,&quot;alt&quot;:&quot;Deployed FAQ assistant answering a user question in Slack, with thumbs up and thumbs down buttons for collecting feedback on the answer&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Deployed FAQ assistant answering a user question in Slack, with thumbs up and thumbs down buttons for collecting feedback on the answer" title="Deployed FAQ assistant answering a user question in Slack, with thumbs up and thumbs down buttons for collecting feedback on the answer" srcset="https://substackcdn.com/image/fetch/$s_!5B7m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 424w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 848w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 1272w, https://substackcdn.com/image/fetch/$s_!5B7m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6cbce2c-a920-4eaa-8855-6710133b7c1b_1448x945.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The deployed agent in front of real users, with feedback buttons under every answer</figcaption></figure></div><h2><strong>Online evaluation</strong></h2><p>We have a system for offline evaluations: we run the agent against our dataset and we use the judge to score the output to see how well it behaves.</p><p>It&#8217;s okay, but it&#8217;s not scalable. Instead, we want to run it continuously: every time somebody uses our agent, we react in real time - save the logs, evaluate them, and display the results on the dashboard.</p><p>We can&#8217;t really continue using our coding agent for that, so we&#8217;ll need to create a stand-alone judge.</p><p>It will give you a real-time view of the agent&#8217;s quality: the performance on live traffic, and new cases that you can add to the gold standard dataset as they appear.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PHhl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PHhl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 424w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 848w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 1272w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PHhl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png" width="1491" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1491,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1664069,&quot;alt&quot;:&quot;Grafana dashboard for online evaluation with a good vs bad time series over 24 hours, a pie chart of judge verdicts, and a live traffic table showing each question with its verdict, score, and reason&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Grafana dashboard for online evaluation with a good vs bad time series over 24 hours, a pie chart of judge verdicts, and a live traffic table showing each question with its verdict, score, and reason" title="Grafana dashboard for online evaluation with a good vs bad time series over 24 hours, a pie chart of judge verdicts, and a live traffic table showing each question with its verdict, score, and reason" srcset="https://substackcdn.com/image/fetch/$s_!PHhl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 424w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 848w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 1272w, https://substackcdn.com/image/fetch/$s_!PHhl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0efa17bd-ebef-451a-b90e-30f97e7690b6_1491x803.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Online evaluation dashboard: judge evaluates the live traffic</figcaption></figure></div><p>Periodically inspect bad results, and if you see something new, add these cases to your gold standard dataset. Also sample the good results to make sure they are actually good.</p><p>For inspecting this live traffic I also often use coding assistants. I take a sample of the data, save it locally and ask them to go through these cases with me.</p><h2><strong>Multiple judges</strong></h2><p>We started with a good/bad evaluator, but I didn&#8217;t really explain what it means. And it anyways depends on your problem.</p><p>Over time, we will need to have multiple judges, each evaluating the agent from a different angle:</p><ul><li><p>Task completion: the agent actually solved the user&#8217;s problem</p></li><li><p>Relevance/Correctness: the answer is relevant/correct</p></li><li><p>Groundedness: the claims in the answer are supported by the documents in the knowledge base</p></li><li><p>Completeness: the answer includes all the required information</p></li><li><p>Following instructions: the agent follows the instructions correctly</p></li><li><p>Trajectory optimality: the sequence of tool calls the agent used to complete the task is an optimal one</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!unlv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!unlv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 424w, https://substackcdn.com/image/fetch/$s_!unlv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 848w, https://substackcdn.com/image/fetch/$s_!unlv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 1272w, https://substackcdn.com/image/fetch/$s_!unlv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!unlv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png" width="1120" height="760" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:760,&quot;width&quot;:1120,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;One agent run fanning out to six judges - task completion, correctness, groundedness, completeness, instructions, trajectory - which all feed into a single scorecard&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="One agent run fanning out to six judges - task completion, correctness, groundedness, completeness, instructions, trajectory - which all feed into a single scorecard" title="One agent run fanning out to six judges - task completion, correctness, groundedness, completeness, instructions, trajectory - which all feed into a single scorecard" srcset="https://substackcdn.com/image/fetch/$s_!unlv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 424w, https://substackcdn.com/image/fetch/$s_!unlv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 848w, https://substackcdn.com/image/fetch/$s_!unlv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 1272w, https://substackcdn.com/image/fetch/$s_!unlv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33683d03-f316-4572-8a12-5cac4bd19756_1120x760.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">One agent run, several judges, one metric per dimension</figcaption></figure></div><p>There are other criteria too that you can consider for your application.</p><p>But I usually recommend starting with one judge and then adding others as your system matures.</p><h2><strong>The agent evaluation checklist</strong></h2><p>As a summary for this article, I wanted to give you a checklist that you can use next time you work on a new agent.</p><pre><code><code>Gold standard dataset:

- [ ] Vibe-check the agent and log 10-15 interactions
- [ ] Label results as "good" or "bad" yourself
- [ ] Define equivalence partitions and test cases within each
- [ ] Include boundary cases (exact match, paraphrase, multiple matching documents, off-topic)
- [ ] Generate synthetic questions

The judge:

- [ ] judge.md describes the classification rules in generic terms, not specific to your examples
- [ ] The judge runs as a subagent that reads only judge.md
- [ ] Check the judge's answers with "agree" or "disagree"
- [ ] Use the fraction of "good" results as your metric
- [ ] Add more judges as your system matures

Improving the agent:

- [ ] The evaluation pipeline is automated: run the agent, run the judge, get the metric
- [ ] Each time you make a change, run the full evaluation set to catch regressions

Online evaluation:

- [ ] Put the agent in front of real users
- [ ] Collect user feedback
- [ ] Score online traffic with a stand-alone judge, show results on a dashboard
- [ ] Inspect bad results periodically, add new failing cases to the eval dataset</code></code></pre><p></p><p>If you want to learn more about building AI Agents, I teach that in my course <a href="https://maven.com/alexey-grigorev/from-rag-to-agents">AI Engineering Buildcamp: From RAG to Agents</a>. In the course, I also cover evaluation in a lot of detail with real code, real tools, and real agents.</p><p>The next cohort starts September 21. You can use code &#8220;SUBSTACK&#8221; to get 20% off. See you there!</p>]]></content:encoded></item><item><title><![CDATA[Deploy a Full-Stack App with AI Coding Assistants]]></title><description><![CDATA[Part 3 of AI Dev Tools Zoomcamp]]></description><link>https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding</link><guid isPermaLink="false">https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Mon, 10 Aug 2026 08:32:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4sUJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the third article in a series for <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>, the free course we run at DataTalks.Club.</p><p>All articles in the series:</p><ul><li><p>Part 1: <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://alexeyondata.substack.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a> (this article)</p></li><li><p><span>Part 4: </span><a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: <a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div id="youtube2-gxt5ZDVnBMM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;gxt5ZDVnBMM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/gxt5ZDVnBMM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>In this article, I want to take a web application and make it accessible for everyone on the internet.</p><p>We will:</p><ul><li><p>Take the application that&#8217;s already working locally</p></li><li><p>Containerize the application</p></li><li><p>Switch from SQLite to Postgres</p></li><li><p>Add integration tests</p></li><li><p>Deploy to AWS</p></li><li><p>Set up CI/CD via GitHub Actions</p></li></ul><p>It&#8217;s based on the second half of the full-day workshop I did for AI Shipping Labs: <a href="https://aishippinglabs.com/workshops/full-stack-vibe-coding">Build and Deploy a Full-Stack App with AI Coding Assistants</a>.</p><h2><strong>Recap</strong></h2><p>In the <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">previous article</a>, we started building an application for system-design interviews. An interviewer creates a session and shares a link with a candidate. When the candidate updates something on the canvas, the interviewer sees the updates in real time.</p><ul><li><p>First, we created the frontend only with React</p></li><li><p>Then we created an OpenAPI specification for defining the frontend-backend API</p></li><li><p>Next, we created the backend from this specification</p></li><li><p>We added database support with SQLite and SQLAlchemy</p></li></ul><p>You can find the code in the <a href="https://github.com/alexeygrigorev/interview-canvas-share">interview-canvas-share</a> repository.</p><h2><strong>Containerization</strong></h2><p>When we run the application locally, we need to execute two commands: one for the frontend and one for the backend.</p><p>Let&#8217;s run them in two separate terminals:</p><pre><code><span># first terminal</span>
<span>cd</span> frontend
npm run dev

<span># second terminal</span>
make run</code></pre><p>So, we have two services, and we need to deploy them to production. We may think that we need two containers: one for the frontend and one for the backend.</p><p>During development, we run the frontend as a separate service because we use Vite. Vite watches the frontend code and refreshes the page when we make changes. It&#8217;s very convenient because we can see our changes immediately.</p><p>In production, the frontend code doesn&#8217;t change while the application is running. We build it once and get a set of static HTML, CSS, and JavaScript files.</p><p>This means we don&#8217;t need a separate container for the frontend: the backend can serve these files. So, we need only one container.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_QEl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_QEl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 424w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 848w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 1272w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_QEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png" width="1110" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1110,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Three stages show the Vite frontend and FastAPI backend running separately in development, the frontend compiling into static files, and one application container serving the frontend and backend in production&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Three stages show the Vite frontend and FastAPI backend running separately in development, the frontend compiling into static files, and one application container serving the frontend and backend in production" title="Three stages show the Vite frontend and FastAPI backend running separately in development, the frontend compiling into static files, and one application container serving the frontend and backend in production" srcset="https://substackcdn.com/image/fetch/$s_!_QEl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 424w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 848w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 1272w, https://substackcdn.com/image/fetch/$s_!_QEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c7c551f-c669-4852-af03-4d2fa94916c2_1110x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">In development (1), the frontend and backend run separately. In production (2, 3), FastAPI serves the built frontend from one container</figcaption></figure></div><p>Ask the coding assistant to create it:</p><pre><code><code>Create a Dockerfile that builds the frontend with Node, then builds a Python image with the backend and the frontend static files.

Backend should serve the frontend.</code></code></pre><p>We get a two-stage Docker build:</p><ul><li><p>First, we use a Node.js image to compile the frontend</p></li><li><p>Then we build the backend and copy only the frontend files without the Node.js dependencies</p></li></ul><p>You can see the <a href="https://github.com/alexeygrigorev/interview-canvas-share/blob/main/Dockerfile">Dockerfile here</a>.</p><p>Build and run the image from the repository root:</p><pre><code>docker build -t sdip:latest <span>.</span>

docker run --rm -p 8000:8000 \
  -v sdip-data:/data \
  -e SDIP_DATABASE_URL=sqlite:////data/sdip.db \
  --name sdip sdip:latest</code></pre><p>Here we specify a named Docker volume <code>sdip-data</code> that will keep the SQLite database between container runs.</p><p>Open the application at <a href="http://localhost:8000/">localhost:8000</a> and test it:</p><ul><li><p>Create an interview session</p></li><li><p>Open the join link in a different browser window</p></li><li><p>Move an element in the candidate window</p></li><li><p>Check that the interviewer sees the change</p></li></ul><p>We&#8217;ll repeat this test again. I&#8217;ll refer to it as the two-session test. </p><p></p><h2><strong>Switch from SQLite to Postgres</strong></h2><p>SQLite is very convenient for local development. It keeps the data in a single file and doesn&#8217;t need a separate database server.</p><p>But for production, we typically use Postgres or a similar database.</p><p>When we set up the foundation <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">in the previous article</a>, we asked the coding agent to use SQLAlchemy.</p><p>I did this on purpose because I knew that later I&#8217;d switch to Postgres.</p><p>Start Postgres locally:</p><pre><code>docker run -d \
  --name interview-canvas-db \
  -e POSTGRES_USER=sdip \
  -e POSTGRES_PASSWORD=sdip \
  -e POSTGRES_DB=sdip \
  -p 5432:5432 \
  -v interview-canvas-pgdata:/var/lib/postgresql/data \
  postgres:16-alpine</code></pre><p>Now we can ask the assistant to use it:</p><pre><code><code>Add Postgres support to the backend.</code></code></pre><p>Run the backend against Postgres from the repository root:</p><pre><code><span>export</span> SDIP_DATABASE_URL=postgresql://sdip:sdip@localhost:5432/sdip
make run</code></pre><p>When it&#8217;s done, repeat the two-session test.</p><h2><strong>Docker Compose</strong></h2><p>Previously, I started a Postgres container with a separate command. But now let&#8217;s put all the services our application needs inside one Docker Compose file.</p><p>With this file, we can run our entire application with a single command <code>docker compose up</code>.</p><p>Ask the assistant to implement it:</p><pre><code><code>Create docker-compose.yaml with two services: Postgres and the app.</code></code></pre><p>The file defines the database and adds a health check, so our application waits until Postgres is ready to accept connections.</p><p>Start it:</p><pre><code>docker compose up --build</code></pre><p>In our case, it runs the application at <a href="http://localhost:8100/">localhost:8100</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e6eo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e6eo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 424w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 848w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 1272w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e6eo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png" width="1110" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:1110,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The application moves from a container using a local SQLite file to Docker Compose with separate application and Postgres services backed by a data volume&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The application moves from a container using a local SQLite file to Docker Compose with separate application and Postgres services backed by a data volume" title="The application moves from a container using a local SQLite file to Docker Compose with separate application and Postgres services backed by a data volume" srcset="https://substackcdn.com/image/fetch/$s_!e6eo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 424w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 848w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 1272w, https://substackcdn.com/image/fetch/$s_!e6eo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49e64777-96c9-41ed-b3ab-a1d6cedfde49_1110x440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">We run the application with Postgres using Docker Compose</figcaption></figure></div><h2><strong>Integration and end-to-end tests</strong></h2><p>The AI assistant might have created some backend tests.</p><p>If it didn&#8217;t, ask it to create integration tests:</p><pre><code><code>Create integration tests that run against docker-compose.yaml. What scenarios should we test?</code></code></pre><p>We added two things that can potentially break, so let&#8217;s test them too. We&#8217;ll verify that:</p><ul><li><p>The frontend compiles correctly</p></li><li><p>The backend can communicate with Postgres</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Th6Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Th6Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 424w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 848w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 1272w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Th6Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png" width="1100" height="650" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:650,&quot;width&quot;:1100,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Playwright controls separate interviewer and candidate browser sessions connected through a shared WebSocket room to FastAPI and Postgres&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Playwright controls separate interviewer and candidate browser sessions connected through a shared WebSocket room to FastAPI and Postgres" title="Playwright controls separate interviewer and candidate browser sessions connected through a shared WebSocket room to FastAPI and Postgres" srcset="https://substackcdn.com/image/fetch/$s_!Th6Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 424w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 848w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 1272w, https://substackcdn.com/image/fetch/$s_!Th6Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f5aa3d7-caf7-48b2-a00c-68245dcd739a_1100x650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Playwright can run the two-session test for us</figcaption></figure></div><p>Ask the AI assistant to implement a test:</p><pre><code><code>Add an end-to-end test that runs against docker-compose.yaml.

Use Playwright to:

1. Log in as the interviewer (session 1).
2. Create an interview session.
3. Share the join link.
4. Join from a separate client as the candidate (session 2).
5. Change the canvas as the candidate (session 2).
6. Verify that the interviewer sees the change (session 1).

Put the tests in the e2e/ folder in the repository root.</code></code></pre><p>After it finishes, we can run the tests with a single make command:</p><pre><code><code>make e2e</code></code></pre><h2><strong>Deploy to AWS</strong></h2><p>We&#8217;re now certain that the application works well. We can deploy it.</p><p>Our application runs in a container and only needs Postgres, so we have a lot of options for deploying it. We can use Render, Railway, Fly.io, or any other managed container system.</p><p><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp/tree/main/cohorts/2025/02-end-to-end">Last year we deployed to Render</a>, but this year I want to deploy to AWS. You don&#8217;t have to use AWS, and instead you can ask the coding assistant to recommend an environment for your application.</p><p>Ask your assistant to deploy it:</p><pre><code><code>Deploy this application to AWS. Use AWS CloudFormation.</code></code></pre><p>For that to work, you need to have an AWS user. I typically create a temporary user with admin permissions and watch every step of what the coding agents are doing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4sUJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4sUJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 424w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 848w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 1272w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4sUJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png" width="1180" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:1180,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A public browser connects to Caddy over HTTPS and WebSockets; Caddy, the application, Postgres, and its pgdata volume run inside one EC2 boundary managed by CloudFormation&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A public browser connects to Caddy over HTTPS and WebSockets; Caddy, the application, Postgres, and its pgdata volume run inside one EC2 boundary managed by CloudFormation" title="A public browser connects to Caddy over HTTPS and WebSockets; Caddy, the application, Postgres, and its pgdata volume run inside one EC2 boundary managed by CloudFormation" srcset="https://substackcdn.com/image/fetch/$s_!4sUJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 424w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 848w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 1272w, https://substackcdn.com/image/fetch/$s_!4sUJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbf2a4c2-b165-4212-815d-9c5e66751e99_1180x510.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">One EC2 instance runs Caddy, the app, and Postgres. We manage it through CloudFormation.</figcaption></figure></div><p>You can see what I got <a href="https://github.com/alexeygrigorev/interview-canvas-share/blob/main/deploy/aws/sdip-stack.yaml">here</a>. It runs the app, Postgres, and <a href="https://caddyserver.com/">Caddy</a> (adds HTTPS and WSS support for our app) on one EC2 instance.</p><p>It&#8217;s fine for a proof-of-concept, but using managed database services (such as RDS) is better. We will not do it here.</p><h2><strong>CI/CD with GitHub Actions</strong></h2><p>We used a user with the admin permissions to deploy the application. It&#8217;s okay for the first deployment, but only when we carefully watch it. The next step is to configure CI/CD and remove that access.</p><ul><li><p>Continuous integration (CI) means that every time we make a change and push it to GitHub, we automatically run all the tests to make sure we didn&#8217;t break anything.</p></li><li><p>Continuous deployment (CD) is about deploying this change automatically.</p></li></ul><p>In GitHub, we use GitHub Actions for that.</p><p>Let&#8217;s configure it. Every time we make a push to main, we want to:</p><ul><li><p>run frontend and backend tests</p></li><li><p>build the containers</p></li><li><p>run the integration tests</p></li><li><p>run the end-to-end tests</p></li><li><p>if all the tests pass, deploy the new version</p></li></ul><p>For the last step, the runner (the process that will deploy the application) will need to be able to access our AWS infrastructure. We will use OpenID Connect (OIDC) for this: the runner will assume a role with the necessary permissions and update the application.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!toEY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!toEY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 424w, https://substackcdn.com/image/fetch/$s_!toEY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 848w, https://substackcdn.com/image/fetch/$s_!toEY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 1272w, https://substackcdn.com/image/fetch/$s_!toEY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!toEY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png" width="1440" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A push to main starts frontend and backend tests in parallel, then builds the Docker Compose stack, runs integration and end-to-end tests, and deploys to AWS through OIDC&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A push to main starts frontend and backend tests in parallel, then builds the Docker Compose stack, runs integration and end-to-end tests, and deploys to AWS through OIDC" title="A push to main starts frontend and backend tests in parallel, then builds the Docker Compose stack, runs integration and end-to-end tests, and deploys to AWS through OIDC" srcset="https://substackcdn.com/image/fetch/$s_!toEY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 424w, https://substackcdn.com/image/fetch/$s_!toEY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 848w, https://substackcdn.com/image/fetch/$s_!toEY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 1272w, https://substackcdn.com/image/fetch/$s_!toEY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce4dae7-81ce-4916-b27f-3d0fa1d6ef7c_1440x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">GitHub Actions runs frontend and backend tests in parallel, checks the full stack, and then deploys to AWS</figcaption></figure></div><p>Create the role and the workflow:</p><pre><code><code>Create a CI/CD pipeline that:

- runs backend and frontend tests in parallel
- builds the Docker Compose stack and runs integration and end-to-end tests against it
- deploys to AWS using a GitHub OIDC role
- validates that the deploy is successful by checking the health endpoint</code></code></pre><p>The <a href="https://github.com/alexeygrigorev/interview-canvas-share/blob/main/.github/workflows/ci.yml">finished workflow</a> uses a restricted AWS role for deployment.</p><p>Change something in the application, commit, and push to see it go live.</p><h2><strong>Clean up</strong></h2><p>When we&#8217;re done, we need to delete all the resources created by CloudFormation:</p><pre><code>aws cloudformation delete-stack --stack-name sdip
aws cloudformation <span>wait</span> stack-delete-complete --stack-name sdip</code></pre><p></p><h2><strong>The next step after deployment</strong></h2><p>Let&#8217;s recap what we have done so far:</p><ul><li><p>We created the frontend application with React</p></li><li><p>Then we defined the frontend-backend API with OpenAPI specs</p></li><li><p>Based on the specs, we created the backend</p></li><li><p>Next, we added database support with SQLite and SQLAlchemy to the backend</p></li><li><p>To make it easier to deploy the application, we put both frontend and backend inside one container. The backend serves the frontend.</p></li><li><p>To go to production, we replaced SQLite with Postgres.</p></li><li><p>Next, we simplified running everything locally with Docker Compose</p></li><li><p>Once everything was in Compose, we created an end-to-end test</p></li><li><p>We took the application that worked locally and deployed it to AWS using CloudFormation</p></li><li><p>Finally, we created a CI/CD deployment pipeline to deploy every change automatically.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oFTO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oFTO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 424w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 848w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 1272w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oFTO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png" width="1180" height="650" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:650,&quot;width&quot;:1180,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Separate interviewer and candidate browser sessions connect through Caddy to the React and FastAPI application, which stores sessions and canvas state in Postgres and its pgdata volume inside one EC2 instance&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Separate interviewer and candidate browser sessions connect through Caddy to the React and FastAPI application, which stores sessions and canvas state in Postgres and its pgdata volume inside one EC2 instance" title="Separate interviewer and candidate browser sessions connect through Caddy to the React and FastAPI application, which stores sessions and canvas state in Postgres and its pgdata volume inside one EC2 instance" srcset="https://substackcdn.com/image/fetch/$s_!oFTO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 424w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 848w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 1272w, https://substackcdn.com/image/fetch/$s_!oFTO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37eae46e-c552-4478-a3ee-f347bab387bd_1180x650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The end-state architecture: two browser sessions connect through Caddy to the app and persistent Postgres data</figcaption></figure></div><p>But we still need to do a lot more for the app to run reliably:</p><ul><li><p>Dev and prod environments</p></li><li><p>Observability: logs, metrics, alerts</p></li><li><p>Using AI as the first responder when the application stops working</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!54DZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!54DZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 424w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 848w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 1272w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!54DZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png" width="1456" height="766" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:766,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user pushes code to main and manually promotes a release from development to production; the Observe group collects telemetry and raises alerts, and the Respond group uses an AI agent to create a fix&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user pushes code to main and manually promotes a release from development to production; the Observe group collects telemetry and raises alerts, and the Respond group uses an AI agent to create a fix" title="A user pushes code to main and manually promotes a release from development to production; the Observe group collects telemetry and raises alerts, and the Respond group uses an AI agent to create a fix" srcset="https://substackcdn.com/image/fetch/$s_!54DZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 424w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 848w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 1272w, https://substackcdn.com/image/fetch/$s_!54DZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F739cee86-2195-4336-8ebd-1e424a30e6e1_1520x800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The user promotes releases while separate observe and respond loops detect production problems and create fixes</figcaption></figure></div><p>We&#8217;ll cover it in the next lesson.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5c023f9c-ee7f-4a0c-9c58-7372abcf5aed&quot;,&quot;caption&quot;:&quot;This is the fourth article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;DevOps and Observability for an AI-Built App&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-18T11:54:47.884Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!i4Rw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd1811ce-b404-4c3c-ac2a-f338909c95c0_1830x760.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://aishippingblog.com/p/devops-and-observability-for-an-ai&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:211674506,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:12,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Getting an AI Engineering Job]]></title><description><![CDATA[The 5-step framework that will land you a job]]></description><link>https://aishippingblog.com/p/getting-an-ai-engineering-job</link><guid isPermaLink="false">https://aishippingblog.com/p/getting-an-ai-engineering-job</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Sat, 08 Aug 2026 18:25:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1b652b6c-b174-4918-82f2-4046e84098e2_1200x630.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>One of the first videos I ever published on YouTube was about getting a data science job. I presented the algorithm that you can follow to get hired in data science. Many things have changed since then, but the algorithm that I described still works.</span></p><div id="youtube2-jYYR1fH8k7o" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;jYYR1fH8k7o&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/jYYR1fH8k7o?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>In this article I take that algorithm and update it for AI Engineering. The updates are minimal and you can use this algorithm for any other engineering job. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h1><span>The Job Search Algorithm</span></h1><p><span>The algorithm is straightforward:</span></p><ol><li><p><span>Network: Connect with people</span></p></li><li><p><span>Apply: Apply for jobs even if you don&#8217;t qualify</span></p></li><li><p><span>Build: Create projects and share them online</span></p></li><li><p><span>Interview: Record what you were asked</span></p></li><li><p><span>Reflect: Review and improve</span></p></li><li><p><span>Repeat</span></p></li></ol><p><span>This is a while loop. Continue networking, applying, building, and interviewing until you receive an offer you like.</span></p><div class="image-gallery-embed" data-attrs="{&quot;gallery&quot;:{&quot;images&quot;:[{&quot;type&quot;:&quot;image/svg+xml&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4fda882-960c-4d75-8053-baaa525618b9_812x770.svg&quot;}],&quot;caption&quot;:&quot;The framework is a while loop&quot;,&quot;alt&quot;:&quot;&quot;,&quot;staticGalleryImage&quot;:{&quot;type&quot;:&quot;image/svg+xml&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4fda882-960c-4d75-8053-baaa525618b9_812x770.svg&quot;}},&quot;isEditorNode&quot;:true}"></div><h1><span>Step 1: Network</span></h1><p>Networking helps you understand the market, find opportunities, and learn what companies are actually looking for. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8kFy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8kFy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 424w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 848w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 1272w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8kFy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png" width="599" height="331.17788461538464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1456,&quot;resizeWidth&quot;:599,&quot;bytes&quot;:390646,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8kFy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 424w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 848w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 1272w, https://substackcdn.com/image/fetch/$s_!8kFy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7ece07-35df-460b-b34c-ff365bcf7313_1906x1054.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Screenshot from my talk</figcaption></figure></div><p><span>Use LinkedIn and meetups for that.</span></p><p><span>Talk to people working in your field. Talk to people who work as AI engineers. Ask about the projects they work on, and the technologies they use. And ask if they are hiring and ask for a referral.</span></p><p>If you are a junior, look for other juniors who have recently got a job. Ask what helped them get hired, which companies they targeted, what the interview process involved, and what they would do differently.</p><p>A conversation doesn&#8217;t have to <span>end with a referral. By talking to people you discover companies that hire, understand which skills to develop, improve your search strategy, and meet interesting people.</span></p><h1><span>Step 2: Apply</span></h1><p>Start applying for jobs right now. Many people postpone it because &#8220;they are not ready". You are ready. Apply even if you don&#8217;t qualify.</p><p>Don&#8217;t reject yourself because you are unsure if you&#8217;re qualified. Apply and let the hiring team evaluate your application. If you apply, you have a chance to move forward. If you don&#8217;t, you never will.</p><p><span>When you look at the job requirements, don&#8217;t treat them all as mandatory. The job description always describes the ideal candidate and often these candidates don&#8217;t exist. </span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i1Nx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i1Nx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 424w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 848w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i1Nx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png" width="1382" height="1090" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1090,&quot;width&quot;:1382,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:217492,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F027dd047-22ee-4568-8fc6-d28df4005589_1615x1092.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i1Nx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 424w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 848w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!i1Nx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff99fe152-6fd2-446d-a5a8-1f41aaf55add_1382x1090.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Job descriptions often describe the ideal candidate who doesn&#8217;t exist.</figcaption></figure></div><p>There are, of course, cases where you shouldn&#8217;t apply. If you&#8217;re a junior, but the position is senior, then don&#8217;t apply. If you match only 20% of the requirements, it&#8217;s also a bad sign. If you&#8217;re in doubt, apply.   </p><p><span>If you apply for relevant roles but don&#8217;t hear back, review your CV. Make sure it shows the experience, skills, and projects that are relevant to the positions you apply. </span></p><p><span>Recently I published a workshop at AI Shipping Labs about </span><a href="https://aishippinglabs.com/workshops/tailor-cv-ai-engineering"><span>tailoring your CV for AI engineering roles</span></a><span> that you may find helpful (heads up: it requires a paid subscription). </span></p><h1><span>Step 3: Build</span></h1><p><span>You learn by doing. When you apply, note which skills are required for the positions you apply for, and build projects using these technologies.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_EE_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_EE_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_EE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:851143,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_EE_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_EE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d23ac0-8491-4123-807c-f50b32632c7c_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Another screenshot from my talk</figcaption></figure></div><p>Always timebox these projects. Set the time limit to 10-20 hours and then move on to the next project. At this stage, your goal is to have an awareness of different technologies and an understanding when to use them. You can go deeper when you&#8217;re already working.</p><p><span>I wrote a full guide on building projects with six steps you can follow: </span></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;9b899937-4a75-42ea-a81f-5867f21ddeb7&quot;,&quot;caption&quot;:&quot;When preparing personal plans for AI Shipping Labs members, I often get the question, &#8220;What should I build for my portfolio?&#8221; There are too many options, and no clear way to pick one.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Choosing a Portfolio Project. The Definite Guide&quot;,&quot;publishedBylines&quot;:[{&quot;bestseller_tier&quot;:null,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;id&quot;:2259961,&quot;is_guest&quot;:false,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;}],&quot;post_date&quot;:&quot;2026-07-03T15:17:33.164Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!qTlw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/choosing-a-portfolio-project-the&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:204912260,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:20,&quot;comment_count&quot;:5,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><span>A complete project should include working code in a public GitHub repository and a clear README. The README is the most important document of your project, so make sure you put enough effort into polishing it before you add a project into your portfolio. </span></p><p><span>A few weeks ago I wrote an article about that:</span></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;cbd8e701-1778-44c3-830c-a5f30135622c&quot;,&quot;caption&quot;:&quot;Your README is the first file people read in your project, and sometimes the only one.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;How to Write a Good README&quot;,&quot;publishedBylines&quot;:[{&quot;bestseller_tier&quot;:null,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;id&quot;:2259961,&quot;is_guest&quot;:false,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;}],&quot;post_date&quot;:&quot;2026-07-16T19:40:57.336Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Lao2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/how-to-write-a-good-readme&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:207284212,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:13,&quot;comment_count&quot;:2,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Don&#8217;t build these projects silently. Share what you build and what you learned online.</p><p>Here&#8217;s a suggested sequence of posts for LinkedIn and/or X:</p><ol><li><p>Describe the project idea and the technologies</p></li><li><p>For each milestone, post a screenshot or a code snippet with what you built </p></li><li><p>When the project is ready, announce it and publish the link</p></li><li><p>Optionally, write a blog post </p></li></ol><p>And yes, there&#8217;s another article I wrote about it:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;3eba0eb0-a23a-430f-8bb6-793a5cee3e2e&quot;,&quot;caption&quot;:&quot;One Idea I Want to Share this Week&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Benefits of Learning in Public and Why It Works&quot;,&quot;publishedBylines&quot;:[{&quot;bestseller_tier&quot;:null,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;id&quot;:2259961,&quot;is_guest&quot;:false,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;}],&quot;post_date&quot;:&quot;2026-01-02T07:01:30.583Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!FXiq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6ad71eb-5e9a-44c2-8ea0-b0b862aef333_930x735.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/benefits-of-learning-in-public-and&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:182086832,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:26,&quot;comment_count&quot;:4,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><h1><span>Step 4: Interview</span></h1><p><span>Once your CV gets past the screening stage, you get invited to an interview. </span></p><p>Don&#8217;t treat interviews as exams. You&#8217;re not in school. It&#8217;s a two-way process: they evaluate you, but you also evaluate them. </p><p>Interviewing is a also skill. Your first interview may not be great, but it improves with practice. Each subsequent interview helps you identify gaps in your preparation and see what you should do differently next time.</p><p>Some interviews will lead to rejections. It&#8217;s normal and may happen for many reasons outside of your control: the company chose someone with more suitable experience, hired an internal candidate, changed the role, or froze hiring. Don&#8217;t take the rejections personally, learn from them and move on.</p><div class="image-gallery-embed" data-attrs="{&quot;gallery&quot;:{&quot;images&quot;:[{&quot;type&quot;:&quot;image/jpeg&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bff5a82e-53ee-42dd-ad2a-b0e8882f4903_1670x783.jpeg&quot;}],&quot;caption&quot;:&quot;The interview process&quot;,&quot;alt&quot;:&quot;&quot;,&quot;staticGalleryImage&quot;:{&quot;type&quot;:&quot;image/jpeg&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bff5a82e-53ee-42dd-ad2a-b0e8882f4903_1670x783.jpeg&quot;}},&quot;isEditorNode&quot;:true}"></div><p>The hiring process varies between companies, but typically you will have some of these interviews in the pipeline:</p><ol><li><p>Recruiter interview: a short conversation about your background, motivation, availability, salary expectations, and basic fit</p></li><li><p>Theoretical interview: questions about LLMs, RAG, agents</p></li><li><p>Coding interview: coding, Python, SQL, code review, or practical engineering tasks</p></li><li><p>Project deep dive: a deeper discussion of your experience and past projects</p></li><li><p>System design: defining the architecture of a project</p></li><li><p>Behavioral interview: examples of how you dealt with different situations at work (and outside)</p></li><li><p>Take-home assignment: a small project you complete at home</p></li></ol><p>You probably won&#8217;t get all of them: some stages may be bundled in one round, and other stages may be absent.</p><p>Now let&#8217;s go through each step of the hiring pipeline. In this article, I mostly used the materials from <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineering Field Guide</a>. You will find a lot more relevant information there if you&#8217;re preparing for AI Engineering interviews.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/alexeygrigorev/ai-engineering-field-guide&quot;,&quot;text&quot;:&quot;&#11088; Star the Field Guide repo&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/alexeygrigorev/ai-engineering-field-guide"><span>&#11088; Star the Field Guide repo</span></a></p><p> </p><h2><span>1. Recruiter interview</span></h2><p><span>The recruiter is the first person you speak with during the interview process.</span></p><p>It&#8217;s not a technical interview. They will tell you about the company, the role and ask some basic questions.</p><p><span>You should be ready to answer:</span></p><ul><li><p>&#8220;What do you know about our company?&#8221; Make sure you do some basic research. If you can&#8217;t answer it, the interview process is likely to stop there (it happened to me!).</p></li><li><p>&#8220;What are your salary expectations?&#8221; Research the market and be ready to provide a realistic range. You can dodge this question and delay it to other stages, but I don&#8217;t think it&#8217;s helpful.</p></li><li><p>&#8220;Tell me about yourself&#8221;. Prepare a 3-minutes story. It will be useful at later stages too. </p></li></ul><p>Use this interview to make sure you understand what the interview process looks like and what the expectations are at each stage of the process. Ask for tips how to prepare for these stages. </p><h2><span>2. Theoretical interview</span></h2><p>There will be one or multiple rounds where the company checks if you <span>understand how LLM applications work. They may appear in a dedicated technical interview or as part of the project deep dive or AI system design sessions.</span></p><p><span>For AI Engineering, you will be asked about:</span></p><ul><li><p><span>LLM behavior, prompting, context windows, and structured outputs</span></p></li><li><p><span>RAG, retrieval, reranking, and common ways RAG fails</span></p></li><li><p><span>Agents, tool use, tool design</span></p></li><li><p><span>Evaluation and debugging</span></p></li><li><p><span>Monitoring, cost, latency, and scalability</span></p></li></ul><p><span>Fine-tuning, model training, and transformer internals can appear too, but this only happens for roles that require these skills.</span></p><p>You can see theoretical questions in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/01-theory.md">the &#8220;Theory&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><h2><span>3. Technical interview</span></h2><p><span>Coding rounds generally fall into two categories: implementation and algorithm rounds.</span></p><p><span>Traditionally, for implementation rounds, you&#8217;re given a task like &#8220;implement a web crawler&#8221;. You&#8217;re not expected to deliver a fully working solution, but rather demonstrate your approach to coding. You may need to design (in the &#8220;object-oriented design&#8221; sense of the word) the interaction between different components, define proper abstractions, and then implement only some parts of it. </span></p><p>However, more and more often companies give a vague task and ask you to use AI to implement it. Y<span>ou are expected to produce a reasonably complete working solution. The interviewer evaluates how you interact with the coding agent, how you review the code, test it, correct errors, and refine the implementation.</span></p><p>The algorithm rounds usually involve easy and medium Leetcode-style problems. Hard problems are rare. There is already a lot of information on the Internet on how to prepare for them, so I won&#8217;t spend time on it. Joining Leetcode and solving some problems there definitely helps. </p><p>You can see technical questions in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/02-coding.md">the &#8220;Coding&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><h2>4. Project deep dive</h2><p>Project deep dive interviews assess your level of seniority and check if your CV matches the reality.</p><p>At my previous jobs I regularly conducted these interviews.</p><p>I start by asking the candidate to introduce themselves and then ask what they were doing recently. Our discussion naturally goes into projects they worked on and then I start going deeper with questions: </p><ul><li><p>What problem does this project solve?</p></li><li><p>How does it work?</p></li><li><p>Which part did you build?</p></li><li><p>Who made the technical decisions? How?</p></li><li><p>What went wrong?</p></li><li><p>What would you change if you were implementing this project today?</p></li></ul><p>This is by no means an exhaustive list, but it should give you some ideas of what to expect. </p><p>I recently had a workshop about it at AI Shipping Labs. Based on it, I wrote an article: <a href="https://aishippinglabs.com/blog/ai-engineering-hiring-manager-interview">AI Engineering Hiring Manager Interview</a> (it&#8217;s free).</p><p>You can see deep-dive questions in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/03-project-deep-dive.md">the &#8220;Project Deep-Dive&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><h2><span>5. System design </span></h2><p>If you&#8217;re applying for more senior roles, you&#8217;ll have a system design round. They are very uncommon for junior roles.</p><p>There are two types of system design interviews: &#8220;classical&#8221; system design and AI system design. </p><p>AI engineers are software engineers who specialize in AI, so they are expected to design software systems too. That&#8217;s why one of the interview rounds may be classical system design.</p><p>In these interviews you will need to design a system related to what the company and the prospective team do.</p><p>A food delivery company may ask you to design an order-processing or delivery-tracking system. When I interviewed at Yandex, they asked me to design search. When I interviewed with Meta, they asked me to design a geo-aware recommender system. When I worked at OLX and we were hiring people to join the recommender platform, we were asking to design a recommender system for an online marketplace. </p><p>But for AI engineers there&#8217;s also an AI system design round. <span>Common tasks include designing a document Q&amp;A system, a coding assistant, an AI search engine, or an agent. The specific system will depend on what the team is doing. </span></p><p><span>For these interviews, clarify the requirements, ask which data is available and what&#8217;s the expected scale. After that, sketch the main components of the system.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ygvD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ygvD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ygvD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:914668,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ygvD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ygvD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f795536-ef95-441f-a072-ce5bf3772092_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can see AI system design questions in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/04-ai-system-design.md">the &#8220;AI System Design&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><h2><span>6. Behavioral interview</span></h2><p><span>The behavioral interview checks how you collaborate, make decisions, take responsibility, and handle setbacks. &#8220;Tell me about a time you needed to help a colleague&#8221;. There&#8217;s nothing specific in these interviews for AI engineers. </span></p><p>The best way to prepare for these interviews is to research the company&#8217;s values. You can ask the recruiter about them too. For each value, prepare examples of how you demonstrated them. As a general reference, I recommend <a href="https://www.amazon.jobs/content/en/our-workplace/leadership-principles"><span>Amazon&#8217;s Leadership Principles</span></a><span>.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!maIn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!maIn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 424w, https://substackcdn.com/image/fetch/$s_!maIn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 848w, https://substackcdn.com/image/fetch/$s_!maIn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 1272w, https://substackcdn.com/image/fetch/$s_!maIn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!maIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png" width="1456" height="716" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:716,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:349898,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!maIn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 424w, https://substackcdn.com/image/fetch/$s_!maIn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 848w, https://substackcdn.com/image/fetch/$s_!maIn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 1272w, https://substackcdn.com/image/fetch/$s_!maIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d102dd-872e-40f6-813a-a5b8ff964387_1456x716.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Amazon leadership principles</figcaption></figure></div><p>You can see behavioral questions in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/05-behavioral.md">the &#8220;Behavioral&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><p></p><h2><span>7. Take-home assignment</span></h2><p>In take-home <span>assignments you need to build a small AI application, submit the code, a README, and a working demo. It&#8217;s also typical to have a defense session where you need to present the solution and answer questions about it.</span></p><div class="image-gallery-embed" data-attrs="{&quot;gallery&quot;:{&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;A take-home assignment for an AI engineer position&quot;,&quot;images&quot;:[{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54894fa5-d68a-4870-b223-555fee1530c9_1080x600.png&quot;,&quot;type&quot;:&quot;image/png&quot;}],&quot;staticGalleryImage&quot;:{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54894fa5-d68a-4870-b223-555fee1530c9_1080x600.png&quot;,&quot;type&quot;:&quot;image/png&quot;}},&quot;isEditorNode&quot;:true}"></div><p><span>Before starting, clarify the requirements, ask if you can use AI tools, and what the evaluation criteria are.</span></p><p><span>Before finishing, </span>make sure you have tests, an evaluation set, and a good README that describes the system well.</p><p>Take-home assignments are time-consuming. They are a good addition to your project portfolio, but if you interview with multiple companies, and all of them give you homework, it can add up.</p><p>If you&#8217;re short on time, it&#8217;s okay to decline them. When you do, make sure you have projects in your portfolio that demonstrate the qualities they want to assess. Then you can say <span>&#8220;I unfortunately don&#8217;t have time to work on it, but I have similar projects on GitHub and can walk you through one&#8221;. Don&#8217;t expect it to work with everyone, but some companies may agree to this.</span></p><p><span>When getting a home task, watch out for warning signs like these:</span></p><ul><li><p><span>You get an automated take-home assignment right after applying, even before speaking to anyone.</span></p></li><li><p><span>The company states the task takes two hours, but the requirements suggest it could take several days.</span></p></li><li><p><span>The company won&#8217;t clarify how your submission will be assessed or utilized.</span></p></li></ul><p>In these cases you may spend time working on the test and never hear back from the company after submitting your project. </p><p>You can see examples of tasks in <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/interview/questions/06-home-assignments.md">the &#8220;Home Assignments&#8221; part of AI Engineering Field Guide interview preparation section</a>.</p><p></p><h1><span>Step 5: Reflect (after the interview)</span></h1><p><span>After each interview, write down all the questions you were asked and tasks you were given. What was difficult? Which answers could have been better?</span></p><p><span>You can also talk to AI, describe in detail how the interview went, and ask for feedback. </span></p><p><span>Write that all down in your interview journal.</span></p><p><span>This will help you identify areas where you can improve, and give better answers in the next interviews.</span></p><p><span>If you&#8217;re rejected, ask the company for feedback. Many companies will decline: there&#8217;s a non-zero chance that you may sue them and use this feedback against them, so they won&#8217;t risk it. But some will share it - most likely over a phone call. Also, don&#8217;t take it personally. Rejections are part of the process.</span></p><p>Continue networking, applying for more jobs, keep building and sharing what you learn. </p><p><span>If you get an offer - congratulations! But don&#8217;t rush into accepting it. Evaluate if the package is good and if you will like working there.</span></p><p><span>If you&#8217;re interviewing with other companies, finish the process with them too. It&#8217;s okay to be explicit about it with the company that made the offer. If they are pushing you to accept it as soon as possible, this is a red flag, so consider this carefully before agreeing. You got one offer, so you&#8217;ll definitely get others. </span></p><p><span>Good luck and have fun!</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://alexeyondata.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share Alexey On Data&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://alexeyondata.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Alexey On Data</span></a></p><p></p><h1><span>What I&#8217;ve been working on this week</span></h1><h2><span>1) Building and deploying a website with ChatGPT Work</span></h2><div class="image-gallery-embed" data-attrs="{&quot;gallery&quot;:{&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;images&quot;:[{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/afe3605e-6994-4bc3-9b8c-516219c06ab9_922x2048.jpeg&quot;,&quot;type&quot;:&quot;image/jpeg&quot;},{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0f36030f-fe2f-4616-94af-dc8eba04313c_922x2048.jpeg&quot;,&quot;type&quot;:&quot;image/jpeg&quot;}],&quot;staticGalleryImage&quot;:{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f3bb4c9-7c8d-4632-bae0-b1edfd9b7e0a_1456x720.png&quot;,&quot;type&quot;:&quot;image/png&quot;}},&quot;isEditorNode&quot;:true}"></div><p><span>This week, I </span><a href="https://x.com/Al_Grigor/status/2084662790438998468"><span>experimented</span></a><span> with building a small website using ChatGPT Work.</span></p><p><span>I&#8217;m really impressed. I sent it photos of a music festival brochure (it was Brutal Assault) and asked it to turn them into a website. It extracted the schedule, built a festival planner, and deployed it to a </span><a href="https://brutal-assault-planner.alexey-s-grigoriev.chatgpt.site/"><span>public URL</span></a><span> that I could share with others. </span></p><p><span>I didn&#8217;t even need to run Codex. Everything happened from my phone from the ChatGPT app. It also has built-in authentication (via ChatGPT) and a built-in database.</span></p><p></p><h2>2) FAQ Assistant in Practice</h2><div id="youtube2-CyH61xiYSnk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;CyH61xiYSnk&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/CyH61xiYSnk?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Last week I wrote an article about the FAQ assistant that we use at DataTalks.Club. Then this week I did a webinar about it where I gave a more detailed overview of the system.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5765df06-f658-4162-81b2-e6b7f82e3d4a&quot;,&quot;caption&quot;:&quot;FAQ assistant is the system that we use in DataTalks.Club to help thousands of our students find the answers to their questions faster.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;(Re)Building a FAQ System for DataTalks.Club&quot;,&quot;publishedBylines&quot;:[{&quot;bestseller_tier&quot;:null,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;id&quot;:2259961,&quot;is_guest&quot;:false,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;}],&quot;post_date&quot;:&quot;2026-08-01T22:59:53.228Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!k3pR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/rebuilding-a-faq-system-for-datatalksclub&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:209424783,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:8,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><h2><span>3) Building a full-stack application with AI</span></h2><div id="youtube2-x9dq5nBpDg8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;x9dq5nBpDg8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/x9dq5nBpDg8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><span>I hosted the second workshop for </span><a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp"><span>AI Dev Tools Zoomcamp</span></a><span>. In the first, we talked about </span><a href="https://alexeyondata.substack.com/p/ai-native-development-specifications"><span>specification-driven development, loop and graph engineering</span></a><span>. In the second, I showed how to build an end-to-end application with frontend, backend, and a database. As an example, we built an application for system design interviews.</span></p><p><span>I&#8217;m hosting the notes on Substack, so you can read them here:</span></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;9ab340ee-0e64-437e-86cd-6a18dfd1f2ae&quot;,&quot;caption&quot;:&quot;This is the second article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Build and Ship a Full-Stack App with AI Coding Assistants&quot;,&quot;publishedBylines&quot;:[{&quot;bestseller_tier&quot;:null,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;id&quot;:2259961,&quot;is_guest&quot;:false,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;}],&quot;post_date&quot;:&quot;2026-08-03T12:59:20.309Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!I96k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:209595653,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:11,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><span>There will be more workshops and events for AI Dev Tools Zoomcamp:</span></p><ul><li><p><span>August 10: </span><a href="https://luma.com/v5tjy562">Test, Containerize, and Deploy an AI-Assisted App</a></p></li><li><p><span>August 18: </span><a href="https://luma.com/ycsfxigi">DevOps and Observability for AI-Built Apps</a></p></li><li><p><span>August 24: </span><a href="https://luma.com/a8qa5s2s"><span>Pre-Course Live Q&amp;A</span></a></p></li><li><p><span>August 31: </span><a href="https://luma.com/tsiusx8s"><span>Course launch</span></a></p></li></ul><p>I will publish the materials from all these workshops as Substack articles too.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive new posts</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><span>4) Inference Engineering - AI Shipping Labs book club</span></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zRm8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zRm8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 424w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 848w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 1272w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zRm8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png" width="1456" height="659" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:659,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:549573,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zRm8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 424w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 848w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 1272w, https://substackcdn.com/image/fetch/$s_!zRm8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1793ba4-2dc7-44da-976e-83b935ff3bf3_2107x954.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>We are also starting a book club at AI Shipping Labs.</span></p><p><span>Our first book is Inference Engineering by Philip Kiely. It covers model architecture, hardware, serving software, optimization, cost, reliability, and running inference systems in production. The digital edition is available for free.</span></p><p><span>The kickoff is on August 10 (Monday) at 5:00 PM CET. We will review the book, discuss the reading cadence, and decide together how the sessions and discussions should work.</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/events/44/inference-engineering-book-club-kickoff&quot;,&quot;text&quot;:&quot;Join the kickoff&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/events/44/inference-engineering-book-club-kickoff"><span>Join the kickoff</span></a></p><p></p><h2><span>5) Stock Markets Analytics Zoomcamp</span></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pgeC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pgeC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 424w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 848w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 1272w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pgeC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png" width="1456" height="594" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:594,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:639712,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/210233610?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pgeC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 424w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 848w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 1272w, https://substackcdn.com/image/fetch/$s_!pgeC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F581fe658-a25e-4cb6-8777-abeb375a027c_1987x811.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><span>In DataTalks.Club, we&#8217;re preparing the next cohort of Stock Markets Analytics Zoomcamp. It starts </span>on August 24<span>.</span></p><p><span>The course provides a gentle introduction to stock market trading, Python programming in Google Colab, analytics, and data visualization.</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://pythoninvest.com/course&quot;,&quot;text&quot;:&quot;Register for the course&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://pythoninvest.com/course"><span>Register for the course</span></a></p><div><hr></div><p>Edited <span>by </span><a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[Build and Ship a Full-Stack App with AI Coding Assistants]]></title><description><![CDATA[Part 2 of the AI Dev Tools Zoomcamp series]]></description><link>https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with</link><guid isPermaLink="false">https://aishippingblog.com/p/build-and-ship-a-full-stack-app-with</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Mon, 03 Aug 2026 12:59:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!I96k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the second article in a series based on <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>, the free course we run at DataTalks.Club.</p><p>In the <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">first article</a>, I wrote about turning an idea into a specification. The specification is only the beginning. In this article, we will create an end-to-end application from scratch. We will cover both frontend and backend, and add a database.</p><p>All articles in the series:</p><ul><li><p>Part 1: <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a></p></li><li><p>Part 2: <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a> (this article)</p></li><li><p>Part 3: <a href="https://alexeyondata.substack.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: <a href="https://aishippingblog.com/p/coding-agent-building-blocks-reusable">Coding Agent Building Blocks: Reusable Skills and Specialized Subagents</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div id="youtube2-x9dq5nBpDg8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;x9dq5nBpDg8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/x9dq5nBpDg8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>We will create an application for system-design interviews.</p><p>As an interviewer, you create a session and share the link with the interviewee. They can create diagrams in the app, and you see the changes in real time.</p><p>Both frontends connect to the same room through WebSockets, so the changes appear in both sessions simultaneously. The FastAPI backend handles those events and saves them to SQLite.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uq-e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uq-e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 424w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 848w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 1272w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uq-e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png" width="1050" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;An interviewer and candidate use separate browser sessions connected through a shared WebSocket room to a FastAPI backend backed by SQLite&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An interviewer and candidate use separate browser sessions connected through a shared WebSocket room to a FastAPI backend backed by SQLite" title="An interviewer and candidate use separate browser sessions connected through a shared WebSocket room to a FastAPI backend backed by SQLite" srcset="https://substackcdn.com/image/fetch/$s_!uq-e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 424w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 848w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 1272w, https://substackcdn.com/image/fetch/$s_!uq-e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78808130-42cf-48ca-aa02-f3c4c3611a5d_1050x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Two browser sessions collaborate through one WebSocket-backed interview room</figcaption></figure></div><p></p><p>You can find the code in the <a href="https://github.com/alexeygrigorev/interview-canvas-share">interview-canvas-share</a> repository.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I96k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I96k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 424w, https://substackcdn.com/image/fetch/$s_!I96k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 848w, https://substackcdn.com/image/fetch/$s_!I96k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 1272w, https://substackcdn.com/image/fetch/$s_!I96k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I96k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png" width="1456" height="809" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:809,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:309800,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/209595653?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!I96k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 424w, https://substackcdn.com/image/fetch/$s_!I96k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 848w, https://substackcdn.com/image/fetch/$s_!I96k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 1272w, https://substackcdn.com/image/fetch/$s_!I96k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65018649-b9b5-40f4-9fef-b877197c4621_2880x1600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">AI System Design Canvas: the application we will develop.</figcaption></figure></div><p>It&#8217;s based on my full-day workshop I did for AI Shipping Labs: <a href="https://aishippinglabs.com/workshops/full-stack-vibe-coding">Build and Deploy a Full-Stack App with AI Coding Assistants</a>.</p><h2><strong>Overview</strong></h2><p>Like in the <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">previous article</a>, we start with an idea. As this is only an idea, we turn it into a specification using ChatGPT.</p><p>From there, we start building:</p><ol><li><p>Generate a frontend that uses mocked backend calls.</p></li><li><p>Establish backend-frontend contract by creating OpenAPI specifications from the frontend service layer.</p></li><li><p>Implement the FastAPI backend, connect it to the frontend, and test the application.</p></li><li><p>Add persistence with SQLite and SQLAlchemy.</p></li></ol><p>At each stage we get something concrete that we can test:</p><ul><li><p>First we define the specification and make sure it reflects what we want to build.</p></li><li><p>After that, we use the specs to create a frontend prototype that we can interact with. We mock backend calls so we can test the idea.</p></li><li><p>Then we connect the frontend to the backend. We start with an in-memory store to make sure the frontend-backend connection works.</p></li><li><p>Finally, we add a database so the application keeps its state after a restart.</p></li></ul><p>At the end, we have a working local application that is ready for deployment.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qJIy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qJIy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 424w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 848w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 1272w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qJIy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png" width="1110" height="520" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:520,&quot;width&quot;:1110,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Three architectural snapshots show a mock service replaced by FastAPI and an in-memory store replaced by SQLite&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Three architectural snapshots show a mock service replaced by FastAPI and an in-memory store replaced by SQLite" title="Three architectural snapshots show a mock service replaced by FastAPI and an in-memory store replaced by SQLite" srcset="https://substackcdn.com/image/fetch/$s_!qJIy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 424w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 848w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 1272w, https://substackcdn.com/image/fetch/$s_!qJIy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f825fa0-c9e2-43aa-85ed-c55e9be77a05_1110x520.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The interfaces stay stable while temporary components are replaced one at a time</figcaption></figure></div><p></p><h2><strong>Start with a specification</strong></h2><p>Before building the frontend, we need to describe the application precisely. If we don&#8217;t do it, we will get something that works but we don&#8217;t need.</p><p>For our application, we need to specify:</p><ul><li><p>who creates an interview session</p></li><li><p>how a candidate joins it</p></li><li><p>which components they can place on the canvas</p></li><li><p>how both people see changes in real time</p></li></ul><p>This is called &#8220;Specification-Driven Development&#8221;. We don&#8217;t spend a lot of time here because it was the focus of the previous article <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a>.</p><p>For creating the specification, I always use ChatGPT in dictation mode. Give the assistant as much information as possible at this stage.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hzmv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hzmv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 424w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 848w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 1272w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hzmv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png" width="1456" height="780" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:780,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Using ChatGPT in dictation mode to describe the system-design interview application&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Using ChatGPT in dictation mode to describe the system-design interview application" title="Using ChatGPT in dictation mode to describe the system-design interview application" srcset="https://substackcdn.com/image/fetch/$s_!Hzmv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 424w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 848w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 1272w, https://substackcdn.com/image/fetch/$s_!Hzmv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb83bc8a-17fc-42af-b742-32feeb503ffb_2181x1168.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Dictating the initial application idea to ChatGPT</figcaption></figure></div><p>You can see the result <a href="https://github.com/alexeygrigorev/interview-canvas-share/blob/main/docs/spec.md">here</a>.</p><h2><strong>Frontend First</strong></h2><p>Now we have the specification, but it&#8217;s only text. There are multiple options of what you can do next:</p><ul><li><p>Focus on the database layer, define the entities, and work all the way up through backend to frontend</p></li><li><p>Alternatively, you can start with specifying OpenAPI and define how backend and frontend interact and build both independently from there</p></li><li><p>Or you can focus on the frontend first and then build the rest</p></li></ul><p>All these approaches make sense and have their pros and cons.</p><p>For simple projects that you&#8217;re building alone, I recommend starting with frontend. You can quickly judge if you&#8217;re moving in the right direction, and if it solves your problem or not.</p><p>Treat it as a way to test your idea and your specification, and do it before you build the rest of the application.</p><p>For this step I like using <a href="https://lovable.dev/">Lovable</a>, which generates a React app from one prompt. Claude Design, Replit, v0, and Bolt are similar tools that you can use instead.</p><p>Alternatively, you can start with coding assistants directly and ask them to create a React app (or whatever technology you want).</p><p>I use Lovable because it creates really nice designs. Also, I&#8217;m not a frontend engineer and I don&#8217;t know much about the frontend world. Lovable makes technology choices for me that I know will make sense, while a coding agent can select something randomly.</p><p>Now open any tool of your choice and give it this prompt:</p><pre><code><code>Create a system design interview application. 

[Paste the ChatGPT-generated specification here.]

Centralize every backend call in one services layer, and create a mock
implementation of it so the whole app runs without a real backend.

Add tests.
</code></code></pre><p>This part is quite important:</p><blockquote><p>Centralize every backend call in one services layer, and create a mock implementation of it so the whole app runs without a real backend.</p></blockquote><p>Without it, the agent can do something arbitrary, but here we explicitly say that we want a mock service. Later, it will become the single point of integration of our frontend with backend. And because we mock it, it will work from the beginning.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hqDM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hqDM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 424w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 848w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 1272w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hqDM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png" width="620" height="156" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79236268-03d9-40bd-bead-38fc6884d272_620x156.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:156,&quot;width&quot;:620,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13388,&quot;alt&quot;:&quot;A user interacts with a real frontend whose backend calls are handled by a local mock service&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user interacts with a real frontend whose backend calls are handled by a local mock service" title="A user interacts with a real frontend whose backend calls are handled by a local mock service" srcset="https://substackcdn.com/image/fetch/$s_!hqDM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 424w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 848w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 1272w, https://substackcdn.com/image/fetch/$s_!hqDM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79236268-03d9-40bd-bead-38fc6884d272_620x156.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">The frontend is real and interactive; only its backend service is mocked</figcaption></figure></div><p>Lovable creates a React app in TypeScript. We can interact with it in the Lovable interface.</p><p>Next, save it to GitHub. If you use Lovable:</p><ul><li><p>Select the plus icon in the bottom-left corner</p></li><li><p>Connect your GitHub account.</p></li><li><p>Lovable creates a private repository. I usually change it to public.</p></li></ul><h2><strong>Move the Frontend into the Project</strong></h2><p>Next, we can clone the repository locally.</p><p>For the rest of the project, I want this setup:</p><pre><code><code>/backend     # backend application and its tests
/docs        # supporting documentation
/frontend    # frontend application
AGENTS.md    # instructions for coding agents
openapi.yaml # API agreement</code></code></pre><p>So let&#8217;s create these folders and move all the frontend stuff to &#8220;frontend&#8221;, and the specification we created to &#8220;/docs/spec.md&#8221;.</p><p>After we re-arranged the files, commit the changes.</p><p>At this point, you should also be able to run the application locally and test that things work the way you want. If they don&#8217;t, use a coding agent to fix it.</p><p>To run a project you exported from Lovable:</p><pre><code><span>cd</span> frontend
npm i
npm run dev</code></pre><h2><strong>AGENTS.md</strong></h2><p>We already discussed the importance of <code>AGENTS.md</code> in the first article.</p><p>Let&#8217;s create one for this project too. Place it in the repository root:</p><pre><code><code>for backend, use uv for dependency management. a few useful commands:

uv sync
uv add &lt;PACKAGE-NAME&gt;
uv run python &lt;PYTHON-FILE&gt;

regularly commit code to git</code> </code></pre><p>This is only the starting point and it will change as your project grows.</p><h2><strong>OpenAPI Specifications</strong></h2><p>When creating frontend, we asked the AI assistant to put everything in a centralized service layer. Later we will replace it with actual calls to backend.</p><p>But now we should define the specification - the agreement between frontend and backend. We will use <a href="https://learn.openapis.org/introduction.html">OpenAPI</a> for that.</p><p>This specification gives explicit information about the endpoints, paths, request bodies, response bodies, and authentication rules.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rUI-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rUI-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 424w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 848w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 1272w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rUI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png" width="960" height="182" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:182,&quot;width&quot;:960,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24784,&quot;alt&quot;:&quot;The frontend and backend both connect to a shared OpenAPI contract&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The frontend and backend both connect to a shared OpenAPI contract" title="The frontend and backend both connect to a shared OpenAPI contract" srcset="https://substackcdn.com/image/fetch/$s_!rUI-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 424w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 848w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 1272w, https://substackcdn.com/image/fetch/$s_!rUI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ab5a0e-253f-48db-acc4-79ccb4c78bde_960x182.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">OpenAPI is the explicit contract shared by the frontend and backend</figcaption></figure></div><p></p><p>Let&#8217;s create it:</p><pre><code><code>Read the frontend's API client in frontend/

Create openapi.yaml at the repository root.

Specify the backend this frontend expects: every endpoint, method, path, request body, response body, and which endpoints need authentication.</code></code></pre><p>You can skip this step. But I wouldn&#8217;t recommend it.</p><p>It takes a few minutes, but has many benefits. The backend gets a precise target instead of being inferred from frontend code. Not only we save tokens this way, but also get a clear picture of what exactly the backend needs.</p><p>You can see the result in <a href="https://github.com/alexeygrigorev/interview-canvas-share/blob/main/openapi.yaml">openapi.yaml</a>.</p><h2><strong>The Backend</strong></h2><p>Now we have the OpenAPI specs and we can use this file to create the backend. We will use Python and FastAPI for that. But you can choose any technology you want. </p><p>When I start with frontend, I use a mocked backend and then replace it with a real one. In the same way, for FastAPI backends, I start with a mocked database, and then later change it.</p><p>I do that because I want to make sure the frontend-backend connection works, and until it&#8217;s smooth, I don&#8217;t worry about the persistence.</p><p>Let&#8217;s ask the coding assistant to implement it:</p><pre><code><code>Build a FastAPI backend in backend/ that implements the openapi.yaml spec.

Use an in-memory store and seed it with data so the frontend has something to show. Add authentication with hashed passwords and bearer tokens for the endpoints that need it.

Split the code into modules - routers, models, store, auth

Write tests</code></code></pre><p>After 5-10 minutes, you will have the backend ready.</p><p></p><h2><strong>Makefile</strong></h2><p>When it&#8217;s ready, you can run the application. Normally, for FastAPI, the command is something like that:</p><pre><code><span>cd</span> backend
uv run uvicorn backend.main:app --reload --port 8091</code></pre><p>I&#8217;ll use port 8091 but you can replace it with any other port you like.</p><p>But for me it&#8217;s always hard to remember these commands, so I ask the coding assistant to create a Makefile:</p><pre><code><code>Create a Makefile so I can easily run it.</code></code></pre><p>Then running it is as simple as</p><pre><code>make run</code></pre><p>When it&#8217;s running, open <a href="http://localhost:8091/docs">http://localhost:8091/docs</a> in the browser. You will see the OpenAPI specification of the implemented backend.</p><p>We can compare it with the actual specs. From this point we no longer need the original <code>openapi.yaml</code> - the one that&#8217;s generated by FastAPI is enough.</p><h2><strong>Connecting Frontend and Backend</strong></h2><p>The backend is runnable now, so we can connect it to the frontend:</p><pre><code><code>Switch the frontend to use the real backend client. </code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_p8i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_p8i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_p8i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png" width="900" height="320" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/badb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:320,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user interacts with the frontend, which calls the FastAPI backend backed by a temporary in-memory store&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user interacts with the frontend, which calls the FastAPI backend backed by a temporary in-memory store" title="A user interacts with the frontend, which calls the FastAPI backend backed by a temporary in-memory store" srcset="https://substackcdn.com/image/fetch/$s_!_p8i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!_p8i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbadb6dc7-05a4-444e-8941-2b6c8c15eae4_900x320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Connect the real frontend and backend while keeping persistence mocked</figcaption></figure></div><p>It most likely won&#8217;t work from the first try. You will hist CORS errors and probably some others.</p><p>It&#8217;s also time to test the application thoroughly. Open two browser windows:</p><ul><li><p>Create a session in one and get the join link</p></li><li><p>Use the join link in the second window</p></li><li><p>Interact with the system in the second window and see the changes being propagated to the first one.</p></li></ul><p>If something doesn&#8217;t work, ask the agent to fix it.</p><h2><strong>Database</strong></h2><p>At this point we have a working application with frontend and backend. They can connect to each other.</p><p>But because we don&#8217;t have a real database yet, when the backend is restarted, all the data is lost.</p><p>Let&#8217;s fix it:</p><pre><code><code>Replace the in-memory store with a database. Use SQLite and SQLAlchemy.
Use an environment variable to configure which DB the server should connect to.
Make it database-agnostic - later we will add support for other databases (e.g. Postgres).</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B3gj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B3gj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B3gj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png" width="900" height="320" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:320,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A user interacts with the frontend, which calls the FastAPI backend backed by SQLite&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A user interacts with the frontend, which calls the FastAPI backend backed by SQLite" title="A user interacts with the frontend, which calls the FastAPI backend backed by SQLite" srcset="https://substackcdn.com/image/fetch/$s_!B3gj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 424w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 848w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 1272w, https://substackcdn.com/image/fetch/$s_!B3gj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9394ee4b-f375-4325-a840-75d58f09dad7_900x320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">SQLite replaces the in-memory store without changing the rest of the application flow</figcaption></figure></div><p></p><p>There are a few important parts in this prompt:</p><ul><li><p>First, I ask it to use SQLite. It&#8217;s a very nice and lightweight database for testing the application locally.</p></li><li><p>At some point, you&#8217;ll want to switch to a different database for production, e.g. Postgres. Using SQLAlchemy makes this process very simple.</p></li><li><p>But we also mention that explicitly so the agent doesn&#8217;t use any SQLite specific features in the first version.</p></li></ul><p>After 5-10 minutes you will have a fully working end-to-end application that&#8217;s running locally. Now you can test it: create multiple sessions and see that changes in one are reflected in another. Stop your backend server, start again, and check that all the changes are still there.</p><p>If something doesn&#8217;t work, ask the coding assistant to fix it.</p><h2><strong>Ready for Deployment</strong></h2><p>Now we have a working application:</p><ul><li><p>we turned an idea into a specification</p></li><li><p>created frontend</p></li><li><p>defined the API contract</p></li><li><p>implemented the backend from the contract</p></li><li><p>connected them</p></li><li><p>added SQLite for persistence</p></li></ul><p>But this application is only working locally. Next, we need to deploy it. That&#8217;s something we will do in the next article. We will take what we developed here and turn it into a deployed service.</p><p>We will:</p><ul><li><p>containerize the frontend and backend</p></li><li><p>add integration tests for the full application flow</p></li><li><p>set up CI to run the checks automatically</p></li><li><p>run Postgres with Docker Compose</p></li><li><p>add database migrations</p></li><li><p>deploy the application to a public environment</p></li><li><p>set up CD to deploy changes after the checks pass</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Step-by-Step Approach</strong></h2><p>In this workshop, we created a working application using the step-by-step approach. At the end of each step, we had something functioning:</p><ul><li><p>Clear speciation</p></li><li><p>Working frontend prototype with mock backend</p></li><li><p>Properly integrated backend with mock database</p></li><li><p>Persistence with SQLite</p></li></ul><p>For each step, we started from a new session and drove the agent to produce a working version.</p><p>But we did it one prompt at a time. After the first version is working, it&#8217;s time to introduce the development process. We talked about it in the <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">first article</a>. This process will help you application continue working properly as it grows bigger.</p><h2><strong>Next in the Series</strong></h2><p>In the next modules we continue working with this app:</p><ul><li><p><a href="https://aishippingblog.com/p/deploy-a-full-stack-app-with-ai-coding">Deployment: deploy the application to the cloud and set up CI/CD</a></p></li><li><p><a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability: add observability to the application and use AI as the first line of support when the application breaks</a></p></li></ul><p>You can find the entire course in <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">the course repository</a>. It&#8217;s free.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/DataTalksClub/ai-dev-tools-zoomcamp&quot;,&quot;text&quot;:&quot;&#11088; Star the course repo&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp"><span>&#11088; Star the course repo</span></a></p><p></p><p>This article is based on the the workshop I did for AI Shipping Labs: <a href="https://aishippinglabs.com/workshops/full-stack-vibe-coding">Build and Deploy a Full-Stack App with AI Coding Assistants</a>. In that workshop I created the snake game, deployed it with AWS and configured CI/CD to make sure tests run on every push. Check it out!</p>]]></content:encoded></item><item><title><![CDATA[(Re)Building a FAQ System for DataTalks.Club]]></title><description><![CDATA[Serving 100k+ Learners with a Serverless FAQ Assistant]]></description><link>https://aishippingblog.com/p/rebuilding-a-faq-system-for-datatalksclub</link><guid isPermaLink="false">https://aishippingblog.com/p/rebuilding-a-faq-system-for-datatalksclub</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Sat, 01 Aug 2026 22:59:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!k3pR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>FAQ assistant is the system that we use in DataTalks.Club to help thousands of our students find the answers to their questions faster.</p><p>There are two components:</p><ul><li><p>FAQ curation: building the FAQ dataset from student contributions, Slack discussions, and YouTube transcripts. It lives in <a href="https://github.com/DataTalksClub/faq">DataTalksClub/faq</a>.</p></li><li><p>FAQ retrieval: the Slack bot that answers students&#8217; questions using this dataset. It lives in <a href="https://github.com/DataTalksClub/faq-assistant">DataTalksClub/faq-assistant</a> and runs on AWS Lambda.</p></li></ul><p>In this article, I want to describe these components and the entire project in more detail:</p><ul><li><p>the data sources and how they become the FAQ dataset</p></li><li><p>the architecture and how data flows through it</p></li><li><p>the decisions I made and why I made them</p></li><li><p>how I evaluate each part of the system</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>ZoomcampQABot</strong></h2><p>Yes, I already wrote about the FAQ assistant in <a href="https://alexeyondata.substack.com/p/from-google-docs-to-an-automated">From Google Docs to an Automated FAQ System for DataTalks.Club Courses</a>.</p><p>In that article, I described the <code>ZoomcampQABot</code> bot developed by Alex Litvinov. When it occasionally breaks down, I have to pull in Alex and ask him to fix the issue. Sometimes it would be handling an expired key, or sometimes it&#8217;s fixing a small bug.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1T5W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1T5W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 424w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 848w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 1272w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1T5W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png" width="1384" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:1384,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:130151,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/209424783?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1T5W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 424w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 848w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 1272w, https://substackcdn.com/image/fetch/$s_!1T5W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe34c7820-9cc7-4934-aa23-a6d73821c44d_1384x436.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><code>ZoomcampQABot</code> in action</figcaption></figure></div><p>Two months ago his OpenAI account ran out of money, so the bot stopped working. I had to ping Alex again. I always felt bad that he uses his own money to pay for the project, so I offered to take it over and run it in the DataTalks.Club infra. Typically he&#8217;d decline it, but this time he agreed.</p><p>This is the setup <code>ZoomcampQABot</code> uses:</p><ul><li><p>OpenAI for generating the answers</p></li><li><p>Fly.io for hosting</p></li><li><p>HuggingFace for embeddings</p></li><li><p>Milvus for vector search, running on Zilliz Cloud in production, with four collections and query engines</p></li><li><p>Cohere for reranking</p></li><li><p>Upstash Redis as an embeddings cache</p></li><li><p>LangSmith for feedback logging</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TXb9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TXb9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 424w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 848w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 1272w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TXb9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png" width="1438" height="705" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/506ad431-9850-415b-961a-355e8bb0b133_1438x705.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:705,&quot;width&quot;:1438,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122659,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!TXb9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 424w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 848w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 1272w, https://substackcdn.com/image/fetch/$s_!TXb9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F506ad431-9850-415b-961a-355e8bb0b133_1438x705.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image from <a href="https://github.com/aaalexlit#llm-powered-question-answering-slack-bot">Alex Litvinov&#8217;s GitHub repository</a></figcaption></figure></div><p>The data comes from multiple sources:</p><ul><li><p>the FAQ dataset</p></li><li><p>Slack history</p></li><li><p>Course GitHub repository data</p></li><li><p>YouTube transcripts</p></li></ul><p>Each course has a separate ingest entrypoint on its own cron job in GitHub Actions. YouTube videos are pulled in manually.</p><p>This setup makes a lot of sense, but I couldn&#8217;t port it easily to the DataTalks.Club infra. I wanted to use it in combination with the <a href="https://github.com/DataTalksClub/au-tomator-lambda">Au-Tomator Slack Bot</a> that I described in <a href="https://alexeyondata.substack.com/p/building-and-maintaining-a-slack">Building and Maintaining a Slack Moderation Bot for an 88k-Member Community</a>.</p><p>The Au-Tomator bot runs on Serverless - on AWS Lambda. It&#8217;s been running for years now and I never had to pay for it: it was always under the free tier for AWS Lambda usage. And when I have to pay for it, I expect it to be minimal. So I wanted to run the FAQ bot on Serverless too.</p><p>So I had to redesign the Slack bot. I&#8217;ll describe the result later in the article, but now I&#8217;ll cover the main data source: the FAQ dataset. This dataset influenced many architectural decisions in the assistant, so I want to talk about it first.</p><h2><strong>Part 1: The FAQ Dataset</strong></h2><p>The FAQ dataset is <a href="https://datatalks.club/faq">open for everyone</a>. We host the website via GitHub pages and the data is in the <a href="https://github.com/DataTalksClub/faq">DataTalksClub/faq</a> repository.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zyK0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zyK0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 424w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 848w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 1272w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zyK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png" width="646" height="365.5934065934066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:824,&quot;width&quot;:1456,&quot;resizeWidth&quot;:646,&quot;bytes&quot;:175415,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/209424783?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zyK0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 424w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 848w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 1272w, https://substackcdn.com/image/fetch/$s_!zyK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b94972-4f46-4511-81f2-0bbf4d607698_2800x1584.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The FAQ website at <a href="https://datatalks.club/faq">https://datatalks.club/faq</a> </figcaption></figure></div><p>Previously it lived in a bunch of Google Docs. It was convenient, but this approach had a few problems:</p><ul><li><p>It was frequently vandalized, so I had to manually roll the documents back.</p></li><li><p>I wanted to automate the curation with AI, but using AI in Google Docs is not trivial.</p></li></ul><p>Eventually I moved the data to a bunch of markdown files. I described the migration process in <a href="https://alexeyondata.substack.com/p/from-google-docs-to-an-automated">From Google Docs to an Automated FAQ System for DataTalks.Club Courses</a>.</p><p>Now with AI assistants I spend a lot less time maintaining this dataset, and I can keep it clean and up-to-date.</p><p>There are multiple sources of questions for the FAQ dataset:</p><ul><li><p>The questions that people contributed via GitHub</p></li><li><p>Discussions in Slack</p></li><li><p>Q&amp;A videos on YouTube</p></li></ul><h2><strong>FAQ Automation: User-Contributed FAQ Records</strong></h2><p>Anyone can contribute to the FAQ dataset:</p><ul><li><p>You <a href="https://github.com/DataTalksClub/faq/blob/main/CONTRIBUTING.md">submit an issue</a>, specifying the question, the course and your answer.</p></li><li><p>A GitHub Actions workflow indexes the entire dataset with minsearch.</p></li><li><p>It then searches twice - on the question alone, and on the question and answer together - and combines the two results with reciprocal rank fusion.</p></li><li><p>It sends the results to OpenAI, which returns a structured decision: <code>NEW</code>, <code>UPDATE</code>, <code>DUPLICATE</code> or <code>WRONG_COURSE</code>.</p></li><li><p>For <code>NEW</code> or <code>UPDATE</code>, it opens a pull request.</p></li><li><p>For <code>DUPLICATE</code> or <code>WRONG_COURSE</code>, it closes the issue.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DWMb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DWMb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 424w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 848w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 1272w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DWMb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png" width="1440" height="410" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:410,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;FAQ contribution workflow: an issue triggers dataset indexing, question-only and full-record searches, reciprocal rank fusion, an LLM decision, and either a pull request or issue closure&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="FAQ contribution workflow: an issue triggers dataset indexing, question-only and full-record searches, reciprocal rank fusion, an LLM decision, and either a pull request or issue closure" title="FAQ contribution workflow: an issue triggers dataset indexing, question-only and full-record searches, reciprocal rank fusion, an LLM decision, and either a pull request or issue closure" srcset="https://substackcdn.com/image/fetch/$s_!DWMb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 424w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 848w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 1272w, https://substackcdn.com/image/fetch/$s_!DWMb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d7ec029-5466-4e58-be37-08489a7d498b_1440x410.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">How a user-contributed FAQ issue is classified and handled</figcaption></figure></div><h2><strong>FAQ Automation Evaluations</strong></h2><p>When the first version of the script was created, we didn&#8217;t have any evaluation set. I mostly relied on my gut feeling. But as more people started using it, the number of incorrect decisions increased. I needed to improve it. Naturally, I didn&#8217;t want to fix it blindly and just hope for the best: I needed a proper evaluation framework.</p><p>For creating it, I used those incorrect decisions. I analyzed the issues where I needed to manually adjust the result, and focused on the cases that were sufficiently different from each other.</p><p>Not all decisions made by the script are equal. If the agent makes a mistake in a <code>NEW</code> or <code>UPDATE</code> decision, it&#8217;s easy to correct. I use AI assistants to help me with that.</p><p>But if the decision is <code>DUPLICATE</code> or <code>WRONG_COURSE</code>, the PR will never be created, and the issue will be closed. In this case, I will not even have a chance to review them. Thus, a mistake in this case is way more expensive. Which means, I had to give these cases more importance in the eval set to make sure that these cases don&#8217;t happen often.</p><p>There&#8217;s also a problem with collecting the evals data from historical decisions. The FAQ data is constantly changing, so we have to use a &#8220;leave-one-out&#8221; setup.</p><p>When the script decides that something is <code>NEW</code> and we later use it in the evals, it&#8217;s no longer <code>NEW</code>: the item was already merged into the dataset. So if we run it with the same issue again, it will say <code>DUPLICATE</code> instead of <code>NEW</code>.</p><p>In order to properly test the <code>NEW</code> cases, we therefore need to remove the record from our dataset. So if we have 200 records, and the record <code>D</code> is the one we added, we remove the <code>D</code> and test this case against the remaining 199 records.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lol7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lol7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 424w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 848w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 1272w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lol7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png" width="1140" height="405" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:405,&quot;width&quot;:1140,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Leave-one-out evaluation: remove record D from the current 200-record FAQ dataset, then run the held-out issue D through the FAQ workflow using the remaining 199 records to recover the expected NEW decision&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Leave-one-out evaluation: remove record D from the current 200-record FAQ dataset, then run the held-out issue D through the FAQ workflow using the remaining 199 records to recover the expected NEW decision" title="Leave-one-out evaluation: remove record D from the current 200-record FAQ dataset, then run the held-out issue D through the FAQ workflow using the remaining 199 records to recover the expected NEW decision" srcset="https://substackcdn.com/image/fetch/$s_!Lol7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 424w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 848w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 1272w, https://substackcdn.com/image/fetch/$s_!Lol7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff9f47-f08a-4b75-8528-010a71d49511_1140x405.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><span>Leave-one-out evaluation for a historical NEW decision</span></figcaption></figure></div><p>When I analyzed all my past corrections, the most common error turned out to be incorrect section placement. For example, instead of placing a record about projects in the &#8220;Project&#8221; section, it would place it in &#8220;General&#8221;. My eval set also tests for that.</p><p>Right now I have 61 cases:</p><ul><li><p>38 expecting <code>NEW</code></p></li><li><p>10 expecting <code>DUPLICATE</code></p></li><li><p>7 expecting <code>WRONG_COURSE</code></p></li><li><p>4 not expecting <code>WRONG_COURSE</code></p></li><li><p>2 expecting <code>UPDATE</code></p></li></ul><p>In addition to testing the whole flow, I have a retrieval-only evaluation set. I use this part to test that duplicate detection works correctly. This part is less interesting, so I&#8217;ll skip it. You can read more about it in the <a href="https://github.com/DataTalksClub/faq/#retrieval">project&#8217;s README</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l9wt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l9wt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 424w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 848w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 1272w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l9wt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png" width="1456" height="619" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:619,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Comparison of the two FAQ evaluation suites: a 25-case retrieval suite running in about two seconds with recall at five of 0.840, and a 61-case generation suite running in about two minutes with 42 passing cases on gpt-5.4-nano&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Comparison of the two FAQ evaluation suites: a 25-case retrieval suite running in about two seconds with recall at five of 0.840, and a 61-case generation suite running in about two minutes with 42 passing cases on gpt-5.4-nano" title="Comparison of the two FAQ evaluation suites: a 25-case retrieval suite running in about two seconds with recall at five of 0.840, and a 61-case generation suite running in about two minutes with 42 passing cases on gpt-5.4-nano" srcset="https://substackcdn.com/image/fetch/$s_!l9wt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 424w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 848w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 1272w, https://substackcdn.com/image/fetch/$s_!l9wt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5251fa64-607f-44ac-a20c-5a4d9f4b1469_1832x779.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The retrieval and generation evaluation suites</figcaption></figure></div><p></p><h2><strong>Bulk Reviewing</strong></h2><p>I usually let the PRs accumulate and process them every two weeks.</p><p>For that, I use AI assistants. I have a <a href="https://github.com/DataTalksClub/faq/blob/main/.claude/skills/clear-backlog/SKILL.md">custom skill</a> that lets me go through each PR and merge it as is, or fix it if it&#8217;s not correct.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UUvf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UUvf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 424w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 848w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 1272w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UUvf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png" width="1456" height="444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b212a448-5a52-402b-b01c-4aa950960448_1946x594.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:444,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Claude Code loading the clear-backlog skill, reading the repository conventions, and listing the open pull request backlog&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Claude Code loading the clear-backlog skill, reading the repository conventions, and listing the open pull request backlog" title="Claude Code loading the clear-backlog skill, reading the repository conventions, and listing the open pull request backlog" srcset="https://substackcdn.com/image/fetch/$s_!UUvf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 424w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 848w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 1272w, https://substackcdn.com/image/fetch/$s_!UUvf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb212a448-5a52-402b-b01c-4aa950960448_1946x594.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Starting a bulk FAQ review with the clear-backlog skill</figcaption></figure></div><p>If I come across an interesting error, I ask the assistant to add it to our evals set. I don&#8217;t try to fix it immediately. I wait till we have a few more cases, then I ask the assistant to see if there are simple ways to fix them.</p><p>If some cases can&#8217;t be fixed easily, I don&#8217;t sweat over it. I just let it sit in the evals with a FAIL status. I don&#8217;t want the system prompt to grow too large and handle all the corner cases. But I do want to know that these corner cases exist.</p><h2><strong>Other Sources: Slack and YouTube</strong></h2><p>Our Slack is very active. Course participants are asking questions and helping each other. In many cases, these discussions are worth saving in the FAQ dataset.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f3s8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f3s8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 424w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 848w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 1272w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f3s8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png" width="1456" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;LLM Zoomcamp Slack channel with several course participants asking about project deadlines, submissions, and the number of required projects&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="LLM Zoomcamp Slack channel with several course participants asking about project deadlines, submissions, and the number of required projects" title="LLM Zoomcamp Slack channel with several course participants asking about project deadlines, submissions, and the number of required projects" srcset="https://substackcdn.com/image/fetch/$s_!f3s8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 424w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 848w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 1272w, https://substackcdn.com/image/fetch/$s_!f3s8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36363aff-9ebc-4f42-a21e-fcc4bb87cc57_2004x1084.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Recurring deadline and submission questions in the LLM Zoomcamp Slack</figcaption></figure></div><p>For that I regularly go through all the Slack threads. I ask my AI assistant to go through each thread, and if something is useful, it&#8217;s saved as a new record.</p><p>Also, for each course I run a few live YouTube sessions, for example:</p><ul><li><p>pre-course Q&amp;A session</p></li><li><p>course launch streams</p></li><li><p>occasional office hours</p></li></ul><p>I get the transcript and use AI assistant to extract potential Q&amp;A candidates. If there&#8217;s something new, it also goes to the dataset.</p><h2><strong>Part 1 Overview</strong></h2><p>The main focus of the first part is the dataset curation.</p><p>I review all the PRs that our FAQ automation creates in batches, and use AI to turn Slack discussions and YouTube videos into focused FAQ records.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BOQK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BOQK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 424w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 848w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 1272w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BOQK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png" width="1020" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1020,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Three sources feed the curated FAQ dataset: GitHub issues pass through automated screening and batch review, while Slack threads and YouTube sessions pass through AI-assisted extraction&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Three sources feed the curated FAQ dataset: GitHub issues pass through automated screening and batch review, while Slack threads and YouTube sessions pass through AI-assisted extraction" title="Three sources feed the curated FAQ dataset: GitHub issues pass through automated screening and batch review, while Slack threads and YouTube sessions pass through AI-assisted extraction" srcset="https://substackcdn.com/image/fetch/$s_!BOQK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 424w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 848w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 1272w, https://substackcdn.com/image/fetch/$s_!BOQK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a911a5e-fb49-4194-b67b-243645e0ed8e_1020x420.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Three sources, two curation paths, and one FAQ dataset</figcaption></figure></div><p></p><p>Now let&#8217;s go back to the retrieval side. This FAQ dataset is used as the main data source for the Slack bot.</p><h2><strong>Part 2: Slack Bot</strong></h2><p>When you mention <code>@Au-Tomator</code> (my bot) or <code>@ZoomcampQABot</code> from Alex, both would perform RAG:</p><ul><li><p>Use search to fetch the candidate FAQ records</p></li><li><p>Pass them to OpenAI</p></li><li><p>Return the answer from the LLM and post it in the thread as a reply</p></li></ul><p><code>ZoomcampQABot</code> has a lot of moving parts though. Most of them are not straightforward to deploy to the serverless environment. So I decided to simplify it.</p><p>The first thing I dropped was indexing Slack threads and YouTube videos. I already extract this information and put it directly to the FAQ dataset, so this part is not needed anymore.</p><p>Second, I removed the vector database. I know that it improves retrieval, but at the cost of having to maintain more infra. I want to keep the setup very lean. If the bot is sometimes not right, it&#8217;s not a huge problem: I and other community members can simply correct it in Slack.</p><h2><strong>Zero-Dependency Serverless Deployment</strong></h2><p>My to-go search library for text search is minsearch. I wrote about it in <a href="https://alexeyondata.substack.com/p/minsearch-the-small-search-library">Minsearch: The Small Search Library Behind My RAG Workshops and Courses</a>.</p><p>However, deploying minsearch to AWS Lambda isn&#8217;t trivial. When I created this library, my focus was on teaching the concepts, not on deploying it to serverless. Internally, it users Scikit-Learn and Pandas, and these libraries in turn pull in numpy and scipy. Normally, everyone working with data already has these libraries in their standard setup. But for Lambda, they are too heavy and take well over the 50 MB limit.</p><p>So I decided to replace minsearch with a zero-dependency search engine written entirely in pure-Python. I called it <a href="https://github.com/alexeygrigorev/zerosearch">zerosearch</a>. It has only one optional dependency - <a href="https://github.com/alexeygrigorev/stemlite">stemlite</a>, which is a stemmer I use across all my small search libraries (zerosearch, minsearch and <a href="https://alexeyondata.substack.com/p/how-i-built-sqlitesearch-a-lightweight">SQLiteSearch</a>).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T3yw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T3yw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 424w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 848w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 1272w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T3yw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png" width="1250" height="330" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:330,&quot;width&quot;:1250,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two-bar chart showing approximately 75 MB for the compressed scikit-learn and pandas dependency stack and 8 MB for the deployed Zerosearch bot, with a dashed reference line at the 50 MB AWS Lambda direct-upload ZIP limit&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two-bar chart showing approximately 75 MB for the compressed scikit-learn and pandas dependency stack and 8 MB for the deployed Zerosearch bot, with a dashed reference line at the 50 MB AWS Lambda direct-upload ZIP limit" title="Two-bar chart showing approximately 75 MB for the compressed scikit-learn and pandas dependency stack and 8 MB for the deployed Zerosearch bot, with a dashed reference line at the 50 MB AWS Lambda direct-upload ZIP limit" srcset="https://substackcdn.com/image/fetch/$s_!T3yw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 424w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 848w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 1272w, https://substackcdn.com/image/fetch/$s_!T3yw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38048412-bca6-4b5c-bd0a-36075db972d0_1250x330.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The scientific Python stack exceeds Lambda&#8217;s direct-upload limit; the Zerosearch deployment fits comfortably</figcaption></figure></div><p>Another challenge was Pydantic. We typically use for structured output, and it works fine in usual environments, but it&#8217;s not trivial to get it right for Lambda. It has a compiled Rust core, and you need to make sure the binaries that you send package into your Lambda deployment are compatible. Plus, loading the binaries core and building the model schemas adds to the cold start time.</p><p>So I removed it along with <code>requests</code> and did all the calls to OpenAI using only the standard Python library.</p><h2><strong>Keeping the Index Fresh</strong></h2><p>Then I started thinking about what to do with updating the index. If a record in the FAQ changes, the search needs to reflect it. Alex solved that by pulling in data using a daily cron job. But I wanted to do this as soon as the data in FAQ changes.</p><p>Almost all my projects have a CI/CD workflow: when I push to main, the code change is propagated to a live environment. I usually use it for code changes, but for this project I did the same for data changes too.</p><p>When a push happens, I re-build the whole index, and push it together with the source code in Lambda. It&#8217;s quite small: it&#8217;s 8 MB total.</p><p>Once it&#8217;s deployed, the lambda loads the local updated index, and can serve the fresh data.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FfcX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FfcX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 424w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 848w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 1272w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FfcX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png" width="840" height="330" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:330,&quot;width&quot;:840,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A FAQ and documentation change triggers an index rebuild, and the new index is sent to AWS Lambda while a Slack user exchanges questions and replies with it&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A FAQ and documentation change triggers an index rebuild, and the new index is sent to AWS Lambda while a Slack user exchanges questions and replies with it" title="A FAQ and documentation change triggers an index rebuild, and the new index is sent to AWS Lambda while a Slack user exchanges questions and replies with it" srcset="https://substackcdn.com/image/fetch/$s_!FfcX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 424w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 848w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 1272w, https://substackcdn.com/image/fetch/$s_!FfcX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96c6e9cc-3e09-4603-9220-5a78aa43d802_840x330.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Every push rebuilds and deploys the index used by subsequent Slack requests</figcaption></figure></div><h2><strong>FAQ Assistant Retrieval Evaluation</strong></h2><p>I replaced vector search with text search, and used zerosearch to make it fast and work well in Serverless. But it would still lose to vector search in terms of retrieval quality.</p><p>How can I make sure I squeeze the absolute best from text search? For that I needed to have a proper evaluation set.</p><p>I built the ground truth dataset from real Slack threads:</p><ul><li><p>Get a Slack dump with all the course channels</p></li><li><p>Take all the Slack threads (9,900 of them)</p></li><li><p>Filter them down to keep actual questions</p></li></ul><p>At the end, I had a sample of 130 records:</p><ul><li><p>60 for Data Engineering Zoomcamp</p></li><li><p>40 Stock Markets Analytics</p></li><li><p>30 AI Dev Tools</p></li></ul><p>For each of the questions, I found and marked the correct documents in the index. Then I evaluated the hit-rate and MRR at k=1,3,5.</p><p>Once I had this dataset, I could start experimenting with search optimization.</p><p>I tried multiple options:</p><ul><li><p>Taking raw question as is</p></li><li><p>Keyword expansion</p></li><li><p>Different variants of query rewriting</p></li></ul><p>The best option is to take a Slack message and turn it into a bunch of keywords while preserving exact error messages, tool names, commands and filenames.</p><p>For example, it turns this Slack message:</p><blockquote><p>Does anyone know why minsearch fails when I run <code>uv run python index.py</code>? I get <code>KeyError: 'course'</code> after renaming <code>documents.json</code> to <code>faq.json</code>.</p></blockquote><p>into these keywords:</p><pre><code><code>minsearch FAQ indexing course field "KeyError: 'course'" "uv run python index.py" index.py documents.json faq.json</code></code></pre><p>For query rewriting I use <code>gpt-4o-mini</code> because it&#8217;s the fastest and cheapest model, but for the actual generation I rely on <code>gpt-5.4-mini</code>.</p><h2><strong>FAQ Assistant Generation Evaluation</strong></h2><p>In the previous part I explained how I evaluate search. I also evaluate generation.</p><p>For the evaluation dataset, I collect feedback from Slack. When a bot answers a question, and somebody corrects it or add something extra, it means that the bot couldn&#8217;t answer the question properly. So we can include this question in the evals.</p><p>So far it&#8217;s not large and has a few cases like:</p><ul><li><p>the answer from the bot is incomplete</p></li><li><p>the answer is incorrect</p></li><li><p>the search doesn&#8217;t find anything</p></li></ul><p>Most of the time the way to fix these problems is not tuning the prompt, but fixing the FAQ dataset itself. I check why a record wasn&#8217;t retrieved, or why the answer didn&#8217;t contain the correct information, and then see how I should update the FAQ records to make it work next time.</p><p>Then I&#8217;d fix the record, re-run evaluation, and re-deploy the bot.</p><h2><strong>Deploying with Au-Tomator</strong></h2><p>I use <a href="https://alexeyondata.substack.com/p/building-and-maintaining-a-slack">Au-Tomator</a> to help me with Slack management. So I wanted to use the existing setup, not create a new Slack bot.</p><p>I already have two Lambdas for Au-Tomator:</p><ul><li><p>The router: routes and filters Slack events. It also makes sure we acknowledge the request from Slack quickly within 3 seconds (a requirement from Slack) and forward the request to the next lambda.</p></li><li><p>The automator: the bot itself, it handles all the reactions.</p></li></ul><p>And I added the third one:</p><ul><li><p>The FAQ assistant: rewrites the question, searches the index, and generates the answer</p></li></ul><p>Now when you mention the bot, it first goes to Au-Tomator, and then Au-Tomator sends it to the FAQ assistant. The assistant gets in the question, and send back the answers. Posting the response to Slack is handled by Au-Tomator.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zd0M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zd0M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 424w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 848w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 1272w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zd0M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png" width="1120" height="230" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:230,&quot;width&quot;:1120,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Slack event flow through the existing Au-Tomator router and automator Lambdas to the new FAQ assistant Lambda, followed by the answer returning through the automator and being posted to Slack&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Slack event flow through the existing Au-Tomator router and automator Lambdas to the new FAQ assistant Lambda, followed by the answer returning through the automator and being posted to Slack" title="Slack event flow through the existing Au-Tomator router and automator Lambdas to the new FAQ assistant Lambda, followed by the answer returning through the automator and being posted to Slack" srcset="https://substackcdn.com/image/fetch/$s_!zd0M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 424w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 848w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 1272w, https://substackcdn.com/image/fetch/$s_!zd0M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ea63ef-b5bc-42de-837d-76dc083033e8_1120x230.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Au-Tomator routes Slack events to the FAQ assistant and posts the generated answer back to Slack</figcaption></figure></div><p>As a bonus, now with this setup, I can trigger the FAQ assistant with <code>:faq:</code> reaction. Previously this reaction would only post a message to the Slack thread saying &#8220;Go check the FAQ&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dhoC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dhoC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 424w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 848w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 1272w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dhoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png" width="591" height="161.6015625" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:280,&quot;width&quot;:1024,&quot;resizeWidth&quot;:591,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Slack screenshot: someone reacts to a question with the :faq: emoji, and the only response is Au-Tomator posting a static 'Please check the FAQ' link&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Slack screenshot: someone reacts to a question with the :faq: emoji, and the only response is Au-Tomator posting a static 'Please check the FAQ' link" title="Slack screenshot: someone reacts to a question with the :faq: emoji, and the only response is Au-Tomator posting a static 'Please check the FAQ' link" srcset="https://substackcdn.com/image/fetch/$s_!dhoC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 424w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 848w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 1272w, https://substackcdn.com/image/fetch/$s_!dhoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa165320b-c226-41f5-bce7-9f7302160a3f_1024x280.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Before the migration, the :faq: reaction just dropped a static &#8216;check the FAQ&#8217; link - no answer was generated</figcaption></figure></div><p>Now it actually checks the FAQ and generates the answer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!grvZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!grvZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 424w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 848w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!grvZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg" width="374" height="518.7518518518518" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1498,&quot;width&quot;:1080,&quot;resizeWidth&quot;:374,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Slack screenshot: a member asks when the next courses start, and Au-Tomator replies with the courses section, the events page, Luma and the Google calendar, citing a docs source&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Slack screenshot: a member asks when the next courses start, and Au-Tomator replies with the courses section, the events page, Luma and the Google calendar, citing a docs source" title="Slack screenshot: a member asks when the next courses start, and Au-Tomator replies with the courses section, the events page, Luma and the Google calendar, citing a docs source" srcset="https://substackcdn.com/image/fetch/$s_!grvZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 424w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 848w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!grvZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2142a9-0ef9-40e0-99bb-4760f17bb4c7_1080x1498.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The bot processing the :faq: reaction</figcaption></figure></div><h2><strong>Other Retrieval Sources</strong></h2><p>I don&#8217;t only search the FAQ dataset. I also index the <a href="https://datatalks.club/docs/">DataTalks.Club documentation</a> and the Markdown files from each course repository.</p><p>I combine all three sources into one search index and package it with the FAQ assistant Lambda. In Slack, either a mention or a <code>:faq:</code> reaction triggers Au-Tomator. It sends the question to the assistant and posts the answer back to the thread.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZPft!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZPft!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 424w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 848w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 1272w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZPft!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png" width="840" height="455" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:455,&quot;width&quot;:840,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The FAQ dataset, DataTalks.Club documentation, and Markdown files from course repositories feed one search index in the FAQ assistant Lambda, while a Slack mention or FAQ reaction triggers Au-Tomator to exchange the question and answer with the assistant&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The FAQ dataset, DataTalks.Club documentation, and Markdown files from course repositories feed one search index in the FAQ assistant Lambda, while a Slack mention or FAQ reaction triggers Au-Tomator to exchange the question and answer with the assistant" title="The FAQ dataset, DataTalks.Club documentation, and Markdown files from course repositories feed one search index in the FAQ assistant Lambda, while a Slack mention or FAQ reaction triggers Au-Tomator to exchange the question and answer with the assistant" srcset="https://substackcdn.com/image/fetch/$s_!ZPft!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 424w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 848w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 1272w, https://substackcdn.com/image/fetch/$s_!ZPft!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0a1c704-b640-4236-83da-e724f6ac79b8_840x455.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Three content sources build the index served through Au-Tomator in Slack</figcaption></figure></div><p></p><h2><strong>The Full Picture</strong></h2><p>In this article, we discussed the two parts of the FAQ system:</p><ul><li><p>The FAQ dataset, where we primarily talked about the dataset curation</p></li><li><p>The FAQ assistant, which is a Slack bot deployed via AWS Lambda</p></li></ul><p>The FAQ assistant reads data from the FAQ dataset and helps students find answers to their questions faster.</p><p>This is how the entire system looks like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k3pR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k3pR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 424w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 848w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 1272w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k3pR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png" width="1120" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:1120,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Full FAQ assistant system: GitHub proposals, Slack discussions, and YouTube sessions become curated FAQ records. The FAQ joins the docs website and course repository Markdown in a ZeroSearch index deployed with the FAQ assistant Lambda. Slack threads use Au-Tomator to exchange questions and answers with the assistant&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Full FAQ assistant system: GitHub proposals, Slack discussions, and YouTube sessions become curated FAQ records. The FAQ joins the docs website and course repository Markdown in a ZeroSearch index deployed with the FAQ assistant Lambda. Slack threads use Au-Tomator to exchange questions and answers with the assistant" title="Full FAQ assistant system: GitHub proposals, Slack discussions, and YouTube sessions become curated FAQ records. The FAQ joins the docs website and course repository Markdown in a ZeroSearch index deployed with the FAQ assistant Lambda. Slack threads use Au-Tomator to exchange questions and answers with the assistant" srcset="https://substackcdn.com/image/fetch/$s_!k3pR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 424w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 848w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 1272w, https://substackcdn.com/image/fetch/$s_!k3pR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82829a1f-3a47-4545-a55a-cbf236ed086d_1120x440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The complete path from community knowledge to a Slack answer</figcaption></figure></div><ul><li><p>I turn GitHub issues, useful Slack discussions, and live-session transcripts into reviewed FAQ records.</p></li><li><p>I combine the FAQ, docs website, and course-repository Markdown into a zerosearch index. The index is rebuilt every time we push to the FAQ repo.</p></li><li><p>A mention or <code>:faq:</code> reaction triggers Au-Tomator that sends the question to the FAQ assistant and publishes the answer in Slack.</p></li><li><p>I use wrong and missing answers as evaluation cases and FAQ updates, so the next index contains the correction.</p></li></ul><h2><strong>Curation over Complexity</strong></h2><p>This year, I re-recorded the entire LLM Zoomcamp course from scratch. There, we use <a href="https://github.com/DataTalksClub/llm-zoomcamp/blob/main/01-agentic-rag/lessons/04-dataset.md">the FAQ dataset</a> as the main running example.</p><p>When I introduce the FAQ dataset, and we do the first RAG example, I mention that the example looks very simple. But the reason it looks so simple is because the complex work has already happened: the dataset was already cleaned and prepared. In reality, in your AI projects, most of the time will go into data cleaning and data curation.</p><p>In this article, I tried to explain how exactly I did it.</p><p>We can always have a better model, use a vector database, or a faster machine, but none of that will help if your data is missing, outdated or unclear.</p><p>When the assistant gives a bad answer, it&#8217;s not because of the tech stack, but because of the data. So the most useful fix to make it better is to improve the dataset.</p><p>In the FAQ case, I collect the feedback from real users, use it to improve the underlying dataset and thus improve the system itself:</p><ol><li><p>Students contribute issues to the FAQ dataset.</p></li><li><p>Useful discussions in Slack become curated FAQ records.</p></li><li><p>The updated dataset is indexed and deployed with the Lambda.</p></li><li><p>Au-Tomator uses it to answer future questions.</p></li><li><p>Incorrect or incomplete answers become evaluation cases and help improve the system.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HV8m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HV8m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 424w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 848w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 1272w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HV8m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png" width="1100" height="550" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:550,&quot;width&quot;:1100,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82765,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/209424783?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HV8m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 424w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 848w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 1272w, https://substackcdn.com/image/fetch/$s_!HV8m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36c8fa86-4f2a-492d-acb7-7a799f2576de_1100x550.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because of this loop, I&#8217;m comfortable with a simpler architecture that I described in this article.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[What AI Forward-Deployed Engineers Do]]></title><description><![CDATA[An Analysis of 113 AI FDE Job Postings]]></description><link>https://aishippingblog.com/p/what-ai-forward-deployed-engineers</link><guid isPermaLink="false">https://aishippingblog.com/p/what-ai-forward-deployed-engineers</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Sat, 25 Jul 2026 08:35:43 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/022df211-7fed-425c-bccf-fe3a41efd637_1600x817.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>Forward-deployed engineering became a growing part of the AI engineering market in 2026.</span></p><p><span>Since January 2026 </span><a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/tree/main/job-market/data_structured">I scrape AI Engineering jobs</a> monthly for <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/"><span>AI Engineering Field Guide</span></a><span>. So far we have 4,894 descriptions, and among them, the number of FDE-related postings increased from 28 in January to 108 in July.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>According to this dataset, the AI Engineering job market doubled in these 6 months. But the number of FDE listings grew 4 times! That is, it&#8217;s growing at twice the overall AI Engineering market rate.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tGZG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tGZG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 424w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 848w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 1272w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tGZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png" width="824" height="371" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:371,&quot;width&quot;:824,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tGZG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 424w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 848w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 1272w, https://substackcdn.com/image/fetch/$s_!tGZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7f03a95-3615-4a28-97d0-80c9e6bfcac5_824x371.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Live FDE job postings nearly quadrupled between January and June 2026</figcaption></figure></div><p><span>In this article, I analyze 113 FDE postings to understand what this work involves. Since our dataset focuses on AI Engineering roles, it&#8217;s limited to FDEs working in AI.</span></p><p><span>We discuss</span></p><ul><li><p><span>how companies define the role</span></p></li><li><p><span>which skills they expect</span></p></li><li><p><span>how FDE differs from applied AI engineering, solutions engineering, and consulting</span></p></li></ul><p>At the end of the article, we also include a quick self-assessment checklist to help you determine whether the FDE role is for you.</p><p></p><h2><span>Forward-Deployed Engineer</span></h2><p><span>Palantir created the forward-deployed engineering model in 2006. They had a general platform, but making it useful required adapting it to each customer&#8217;s data, workflows, and infrastructure.</span></p><p><span>To solve it, they placed their engineers in their customer&#8217;s teams. FDEs identified their problem, configured Palantir&#8217;s platforms, built integrations and custom components. They also supported product development: when the same problem appeared across deployments, Palantir would integrate this as a feature to the main platform.</span></p><p><span>Palantir&#8217;s FDE model applies directly to AI in enterprises today. General-purpose models must be adapted to each customer&#8217;s data, systems, and workflows before they can deliver value in production.</span></p><p><span>There&#8217;s a &#8220;deployment gap&#8221; - the gap between a prototype and a working customer system. It exists because many enterprises don&#8217;t have the engineering capacity or product knowledge to close it themselves. FDEs fill that gap.</span></p><p>A forward-deployed engineer is a customer-facing software engineer who turns an ambiguous business problem into a working production system. They work alongside the customer to define the problem, design the solution, adapt and integrate the company&#8217;s product, build any missing software, deploy and debug the system, and feed recurring customer needs back into the core product.</p><h2><span>FDE&#8217;s responsibilities: our analysis</span></h2><p><span>We collected a dataset of 113 FDE jobs descriptions from January to July, and </span><a href="https://github.com/alexeygrigorev/ai-engineering-field-guide/blob/main/role/06-fde.md"><span>then analyzed the expected responsibilities</span></a><span>.</span></p><p><span>They include:</span></p><ul><li><p><span>Direct client engagement (90% of postings)</span></p></li><li><p><span>Building and deploying production system (87% of postings)</span></p></li><li><p><span>System, API, and data integration (62% of postings)</span></p></li><li><p><span>Project scoping, discovery, and requirements gathering (51% of postings)</span></p></li><li><p><span>System testing, evaluation, and monitoring (41% of postings)</span></p></li><li><p><span>Channeling customer feedback back into the core product roadmap (39% of postings)</span></p></li><li><p><span>Developing prototypes, demos, and proofs of concept (25% of postings)</span></p></li><li><p><span>Travel or client on-site presence (10% of postings)</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LxRa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LxRa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 424w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 848w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 1272w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LxRa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png" width="1456" height="822" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:822,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:152517,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/208329624?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LxRa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 424w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 848w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 1272w, https://substackcdn.com/image/fetch/$s_!LxRa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf8e481-92d2-4521-8a5f-7aada5234de5_2375x1341.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If we compare it with the rest of the dataset, only 21% of the other AI roles are expected to interact with the clients. </p><p><span>Here&#8217;s how we can describe the FDE&#8217;s main responsibilities</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iXBX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iXBX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 424w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 848w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 1272w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iXBX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png" width="1456" height="443" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:443,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:340612,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/208329624?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iXBX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 424w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 848w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 1272w, https://substackcdn.com/image/fetch/$s_!iXBX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4847ffa8-8bdf-463b-a15b-e458272d6c02_1456x443.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>1) Understand the customer&#8217;s problem</span></h3><p><span>FDEs usually begin with a desired business outcome. They work with users, domain experts, and engineering teams to understand the existing workflow, examine the available data, and identify technical constraints. They then translate what they learn into deployment requirements and success criteria.</span></p><h3><span>2) Build the production AI application</span></h3><p><span>Once the problem is clear, the FDE writes the software to solve it, handling conventional production engineering tasks like authentication, permissions, testing, error handling, deployment, logging, and observability.</span></p><h3><span>3) Connect the application to the customer&#8217;s systems</span></h3><p><span>FDEs also work on integration, and this need is present in 62% of job postings. They build connectors, data pipelines, transform data, configure access controls, integrate APIs, and deploy applications in the customer&#8217;s cloud. </span></p><h3><span>4) Deploy, evaluate, and debug the system</span></h3><p><span>Production deployment expands the FDE&#8217;s role beyond implementation. The engineer must define evaluation criteria for realistic conditions and use logs, traces, metrics, and user feedback to identify failures post-release. FDE continue to be involved in the project after deployment.</span></p><h3><span>5) Bring field findings back to the product</span></h3><p><span>Some deployment issues are unique to individual customers, while others highlight common needs. FDEs identify these recurring problems and relay customer feedback to the product team, with 39% of postings referencing this contribution. This process connects customer delivery and product development, allowing the FDE to adapt the product for one customer and identify reusable capabilities for others.</span></p><h2><span>Skills companies ask for</span></h2><p><span>It&#8217;s natural that with these responsibilities FDEs are expected to have a broad mix of skills. </span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b_gT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b_gT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 424w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 848w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 1272w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b_gT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png" width="1375" height="796" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:796,&quot;width&quot;:1375,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:496973,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/208329624?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b_gT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 424w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 848w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 1272w, https://substackcdn.com/image/fetch/$s_!b_gT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ca161db-1839-4768-8963-e2b61c9d96ae_1375x796.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>Software engineering</span></h3><p><span>The technology stack varies from job to job, but all of the FDEs are expected to deploy production software.</span></p><p><span>Python is the most frequently mentioned programming language, appearing in 89% of job postings. Other commonly referenced languages and frameworks include:</span></p><ul><li><p><span>TypeScript: 29%</span></p></li><li><p><span>SQL: 23%</span></p></li><li><p><span>React: 14%</span></p></li></ul><h3><span>Applied AI</span></h3><p><span>FDEs are expected to build applications with existing models, not train them. The most frequently mentioned gen AI technologies are:</span></p><ul><li><p><span>Prompt engineering: 56%</span></p></li><li><p><span>RAG: 50%</span></p></li><li><p><span>LangChain: 32%</span></p></li><li><p><span>AI agents: 30%</span></p></li><li><p><span>Vector databases: 23%</span></p></li><li><p><span>Agentic workflows: 19%</span></p></li></ul><h3><span>Cloud and deployment</span></h3><p><span>In the job descriptions, the three major cloud providers appear at similar rates:</span></p><ul><li><p><span>AWS: 40%</span></p></li><li><p><span>GCP: 36%</span></p></li><li><p><span>Azure: 32%</span></p></li></ul><p><span>Companies also seek candidates with experience in the following areas:</span></p><ul><li><p><span>Docker: 35%</span></p></li><li><p><span>Kubernetes: 31%</span></p></li><li><p><span>CI/CD: 27%</span></p></li></ul><h3><span>Customer-facing work</span></h3><p><span>The technical requirements represent only part of the role. With 92% of FDE postings identified as customer-facing, engineers must also navigate technical discovery and tackle evolving requirements.</span></p><p><span>FDEs need to be able to:</span></p><ul><li><p><span>Understand existing business workflows</span></p></li><li><p><span>Translate user needs into technical requirements</span></p></li><li><p><span>Communicate constraints and tradeoffs to stakeholders</span></p></li><li><p><span>Collaborate with domain specialists and customer engineering teams</span></p></li><li><p><span>Adapt implementations based on new information</span></p></li><li><p><span>Differentiate customer-specific integration from reusable product gaps</span></p><p></p></li></ul><h2><span>How the role differs from related titles</span></h2><p><span>FDEs have a broad set of responsibilities, so it&#8217;s natural that the role overlaps with many others.</span></p><p><span>The main difference is the type of code FDEs write, how long they work with one customer, and the involvement in production deployment. </span></p><p>An FDE builds and deploys systems for specific customers, writes production code, and stays involved until the deployment works. </p><ul><li><p>Consultant: Works within a defined engagement and usually leaves after delivering recommendations or an implementation. FDE: Stays through production deployment, owns more of the outcome, and feeds customer needs back into the product.</p></li><li><p>Solutions or sales engineer: Focuses on demos, technical validation, and supporting multiple deals before handing off implementation. FDE: Writes production code, owns deployment, and works more deeply with fewer customers.</p></li><li><p>AI engineer: Builds production AI systems, often with more focus on the underlying product or platform. FDE: Places greater emphasis on customer integration, deployment, and close collaboration.</p></li></ul><p></p><h2><span>Who is a good fit</span></h2><p><span>FDEs come from different technical backgrounds:</span></p><ul><li><p>AI engineer: Strong in LLM applications, RAG, agents, evaluation, and production AI. Gap: Customer discovery, enterprise integration, and ownership of customer-specific deployments.</p></li><li><p>Software or product engineer: Strong in APIs, backend or full-stack development, testing, and shipping software. Gap: Applied AI, evaluation, data pipelines, and customer discovery.</p></li><li><p>Data engineer: Strong in pipelines, integrations, distributed systems, cloud platforms, and production operations. Gap: Full-stack development, LLM systems, evaluation, and product discovery.</p></li><li><p>ML engineer: Strong in model deployment, monitoring, evaluation, and production ML. Gap: Broader product engineering, customer-specific integrations, and direct customer work.</p></li><li><p>Platform, DevOps, or SRE engineer: Strong in infrastructure, CI/CD, security, reliability, observability, and debugging. Gap: Application development, applied AI, customer discovery, and workflow analysis.</p></li><li><p>Solutions engineer or technical consultant: Strong in customer discovery, solution design, technical communication, and varied customer environments. Gap: Production software development, long-term ownership, and applied AI evaluation.</p></li><li><p>Data scientist: Strong in experimentation, model evaluation, analysis, domain knowledge, and stakeholder communication. Gap: Maintainable application development, systems integration, infrastructure, and deployment.</p></li></ul><p>For our dataset and set of responsibilities, <span>AI engineering is the closest technical background because the tools and systems often overlap. (But our data is probably biased because in our scrapes we focus on AI Engineering roles.) </span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iveB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iveB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 424w, https://substackcdn.com/image/fetch/$s_!iveB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 848w, https://substackcdn.com/image/fetch/$s_!iveB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 1272w, https://substackcdn.com/image/fetch/$s_!iveB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iveB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png" width="603" height="492.6951219512195" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:938,&quot;width&quot;:1148,&quot;resizeWidth&quot;:603,&quot;bytes&quot;:916838,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/208329624?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iveB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 424w, https://substackcdn.com/image/fetch/$s_!iveB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 848w, https://substackcdn.com/image/fetch/$s_!iveB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 1272w, https://substackcdn.com/image/fetch/$s_!iveB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b12bfbe-d823-4989-a500-36010b340d8c_1148x938.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The strongest fit is someone who can combine three capabilities:</span></p><ul><li><p><span>Build and operate production software across application code, data, integrations, and infrastructure.</span></p></li><li><p><span>Work directly with customers to turn unclear problems into technical requirements and measurable outcomes.</span></p></li><li><p><span>Remain responsible through deployment, evaluation, and production debugging.</span></p></li></ul><h2><span>Is this role for you?</span></h2><p><span>Forward-deployed engineering may suit you if you want to be a hands-on engineer while working closely with customers and owning projects through production.</span></p><p><span>The role may be less suitable if you prefer narrow technical ownership, stable specifications, limited customer interaction, or handing projects off before production.</span></p><p><span>Before applying, ask yourself:</span></p><ul><li><p><span>Do I want regular customer interaction to be part of my engineering work?</span></p></li><li><p><span>Can I make progress when requirements are incomplete or changing?</span></p></li><li><p><span>Am I comfortable working across several parts of the stack?</span></p></li><li><p><span>Do I want to own deployment and production results, not only implementation?</span></p></li><li><p><span>Can I explain technical constraints to non-specialists?</span></p></li><li><p><span>Am I willing to work within the customer&#8217;s existing systems and infrastructure?</span></p></li><li><p><span>Do I enjoy turning recurring customer problems into reusable product improvements?</span></p></li></ul><p></p><h2><span>Conclusion</span></h2><p><span>FDE is still an inconsistent job title, but we can see a clear focus on customers and production.</span></p><p><span>Compared to traditional AI engineering, FDEs place a stronger, more consistent emphasis on customer discovery, systems integration, and deployment responsibilities. For engineers who want that combination, forward-deployed engineering provides a path to remain technical while owning a broader part of the production outcome.</span></p><p></p><p></p><p><span>Edited by </span><a href="https://www.linkedin.com/in/valeriia-kuka/"><span>Valeriia Kuka</span></a><span>.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[AI-Native Development: Specifications, Loop and Graph Engineering]]></title><description><![CDATA[Part 1 of the AI Dev Tools Zoomcamp series]]></description><link>https://aishippingblog.com/p/ai-native-development-specifications</link><guid isPermaLink="false">https://aishippingblog.com/p/ai-native-development-specifications</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Wed, 22 Jul 2026 06:59:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/VUJxJGpaDEs" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the first article in a series based on <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a>, the free course we run at DataTalks.Club.</p><ul><li><p>Part 1: <a href="https://alexeyondata.substack.com/p/ai-native-development-specifications">AI-Native Development: Specifications, Loop and Graph Engineering</a> (this article)</p></li><li><p>Part 2: <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 3: <a href="https://alexeyondata.substack.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li><li><p>Part 5: TBA</p></li></ul><p>I start with AI-native developer workflows. We will see how to turn a raw idea into a specification, give coding agents the right context, and verify what they build.</p><div class="callout-block" data-callout="true"><p style="text-align: center;">Subscribe to receive the next article in the series.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/subscribe?"><span>Subscribe now</span></a></p></div><div id="youtube2-VUJxJGpaDEs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;VUJxJGpaDEs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/VUJxJGpaDEs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Coding agents now write code faster than I can read it.</p><p>When we give an agent a task, it can quickly implement it. But if a task is vague, the agent fills the gaps with its own assumptions. A weak agent that misunderstands us writes fifty lines of broken code. A strong agent that misunderstands us creates eight files, wires them together, and adds tests that pass. The code works, but it isn&#8217;t what we needed.</p><p>We no longer spend most of our time typing. We spend it saying precisely what we want and checking what came back.</p><p>In this article, I show how to make the request specific so the agents don&#8217;t need to guess. Then we decompose the request into tasks and assign them to a product manager, a software engineer, and a QA engineer. Finally, we implement all the tasks in a backlog through a loop.</p><p>We cover topics like:</p><ul><li><p>Spec-driven development</p></li><li><p>Context engineering</p></li><li><p>Loop engineering</p></li><li><p>Graph engineering</p></li></ul><p>We&#8217;ll use a deliberately vague project idea: a tool for weekly feedback for projects. It doesn&#8217;t say who gives the feedback, who receives it, or what &#8220;projects&#8221; means.</p><p>You can see the final result in the <a href="https://github.com/alexeygrigorev/retroloop">retroloop repository</a>.</p><h2>Specs before code</h2><p>We need to understand what we want to build before the agent produces the first line of code. We have to think it through in detail and give explicit instructions. If we do that, the agent will produce something close to what we want.</p><p>We call this &#8220;spec-driven development&#8221;. We start with the specification, make sure it aligns with our vision, and only then write the code from it.</p><p>In our example, &#8220;a tool for weekly feedback for projects&#8221;, many things aren&#8217;t clear.</p><ul><li><p>Who are the users for this tool?</p></li><li><p>What problem will they solve?</p></li><li><p>How are they going to use it?</p></li></ul><p>If we don&#8217;t specify these things and give the idea directly to a coding agent, it&#8217;ll fill the gaps.</p><p>When I asked Claude Code to implement this project, the only prompt I gave was &#8220;a tool for weekly feedback for projects&#8221;. It came up with <code>weekly-feedback</code>, a command-line tool for tracking weekly project status. It records wins, issues, blockers, and next steps. It also created documentation and covered the app with 62 tests, all of which passed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mvpa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mvpa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 424w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 848w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 1272w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mvpa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png" width="1456" height="503" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:503,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The one-shot weekly-feedback CLI&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The one-shot weekly-feedback CLI" title="The one-shot weekly-feedback CLI" srcset="https://substackcdn.com/image/fetch/$s_!Mvpa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 424w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 848w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 1272w, https://substackcdn.com/image/fetch/$s_!Mvpa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9b81861-8ce0-4ecf-9a9a-1871d756760d_2352x812.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It all works perfectly, but that&#8217;s far from what I needed. I needed a web tool for a team retrospective that captures feedback from teams in the form of &#8220;Start/Stop/Continue&#8221;. It was my fault for not telling Claude that.</p><h2>Start in a chat assistant</h2><p>Instead of giving a prompt directly to the coding assistant, I start in a chat application and talk the idea through. I use ChatGPT in dictation mode for this.</p><p>I begin with the same vague idea:</p><pre><code><code>I want to build a tool for weekly feedback for projects.

Help me set the scope for this project precisely. I want to brainstorm with you
and understand how the tool should work. Give me options.

Ask me one question at a time and keep your output short.</code></code></pre><p>This way, we can use AI as our brainstorming partner and find out precisely what we want:</p><ul><li><p>Who contributes feedback? All team members.</p></li><li><p>What format do they use? Start/Stop/Continue.</p></li><li><p>Is the feedback anonymous? Names appear by default, but contributors can choose to remain anonymous.</p></li><li><p>What can people see before the reveal? Only their own cards.</p></li><li><p>How does the facilitator reveal the feedback? All cards appear at the same time.</p></li><li><p>What happens next? The team clusters the cards, then each person casts three votes for the topics to discuss and can give multiple votes to one topic.</p></li><li><p>What does the team record? Decisions and action items from the discussion.</p></li><li><p>Can the facilitator add a recording? They can upload audio, video, or a transcript after the meeting, but built-in recording isn&#8217;t part of the first version.</p></li></ul><p>When we finish, I ask for a file with all the specifications:</p><pre><code><code>Save everything to a markdown file that I can download.</code></code></pre><p>Download the file and save it as <code>plan.md</code>.</p><h2>Bootstrapping a project</h2><p>Create a project from this specification:</p><pre><code>mkdir project-name
<span>cd</span> project-name

git init</code></pre><p>Copy the <code>plan.md</code> file:</p><pre><code>mkdir -p _docs
mv <span>~</span>/Downloads/plan.md _docs/plan.md
git add _docs/plan.md
git commit -m <span>&#8220;Add project plan&#8221;</span></code></pre><p>You can find the <a href="https://github.com/alexeygrigorev/retroloop/blob/711c33cc5b6b/_docs/plan.md">plan.md file</a> from this project in the Retroloop repository.</p><p>I try to commit as often as possible, after every meaningful decision. With those commits, we can review what the agent changed. If something isn&#8217;t working well, we can easily return to the last good state.</p><h2>Choose the stack and architecture</h2><p>During the brainstorming session, we didn&#8217;t choose the tech stack.</p><p>Ask the coding agent to come up with several options:</p><pre><code><code>Read _docs/plan.md. Propose multiple options for the tech stack and
explain each option.

Don't write code yet.</code></code></pre><p>It proposes multiple options and explains the tradeoffs of each one.</p><p>I choose Django because I know it well enough to review. In your case, you can select any technology you&#8217;re comfortable with. It&#8217;s also okay not to have a preference and to let the agent select what it thinks will work best.</p><h2>Turn the decisions into a backlog</h2><p>Now that we&#8217;ve settled on the tech stack, we can ask the agent to decompose the specifications into a backlog with tasks:</p><pre><code><code>Create a backlog with tasks in _docs/tasks.md.

Each task should be small enough to finish in one session, and
independent enough that I could hand it to someone who has not read
the others.

Use this template for each task:

## &lt;number&gt;. &lt;title&gt;
Goal: &lt;one line&gt;
Description: &lt;two or three sentences on what the task involves&gt;

The first task should be setting up an empty project with a passing test.

Don't write code yet.</code></code></pre><p>It created these <code>tasks.md</code>.</p><p>Review the tasks and ask the agent to merge tasks that are too small or split tasks that don&#8217;t fit into one session. We want to create an MVP - the first version of the app. If something is out of scope for your vision of the MVP, remove it.</p><p>When we&#8217;re happy with the tasks, move them to a task tracker. I use GitHub issues for that.</p><p>Ask the agent to do it:</p><pre><code><code>Create a public GitHub repo for this project.
Move each task from _docs/tasks.md into a GitHub issue.</code></code></pre><p>For that to work, we need the <code>gh</code> CLI tool authenticated and the repo connected to the GitHub remote.</p><p>From this point on, GitHub issues are the canonical tasks and the only active backlog. We no longer need <code>_docs/tasks.md</code>.</p><h2>Context engineering</h2><p>The repository has a backlog now. When we start a new session, however, the agent doesn&#8217;t know which task we mean. It must figure that out every time.</p><p>These details go in <code>AGENTS.md</code>, which coding agents like Codex or OpenCode read when they start a new session.</p><p>Claude Code reads <code>CLAUDE.md</code>, while I use multiple coding assistants and want my workflow to be tool-agnostic.</p><p>That&#8217;s why I also create <code>CLAUDE.md</code> with a single line:</p><pre><code><span>@AGENTS</span>.md</code></pre><p>It tells Claude to read <code>AGENTS.md</code>.</p><p>This is called context engineering. With prompt engineering, we control one message in one session. With context engineering, we control what agents know when they start a new session and what information they can find while they work. We include useful facts and working rules they would otherwise have to rediscover.</p><h2><code>AGENTS.md</code></h2><p>To make this context available in every new session, create <code>AGENTS.md</code>:</p><pre><code><code>Commands

- `uv sync` - install dependencies
- `uv run pytest` - the whole suite
- `uv run pytest tests/test_home.py` - one test file

Rules

- Dependencies are added in `pyproject.toml`. Do not add one without
  asking</code></code></pre><h2>The other documents</h2><p>In addition to <code>AGENTS.md</code>, I usually have a few other Markdown documents in my projects.</p><p>The main one is <code>process.md</code>, which I use to describe how work is organized. It could live inside <code>AGENTS.md</code>, but I keep it separate.</p><p>Create <code>_docs/process.md</code>:</p><pre><code><span>-</span> Tasks are GitHub issues, one at a time
<span>-</span> Read the acceptance criteria before starting and before closing
<span>-</span> Commit regularly</code></pre><p>As I continue working on a project, I may create other documents, such as:</p><ul><li><p><code>testing-guidelines.md</code> for testing</p></li><li><p><code>design-system.md</code> so the UI doesn&#8217;t drift every session</p></li><li><p><code>api.md</code>, which describes what the API should look like</p></li></ul><p>I keep them together in <code>_docs/</code> and link them from <code>AGENTS.md</code>:</p><pre><code>Documents

<span>-</span> <span>`_docs/process.md`</span> - how work is organized
<span>-</span> Before writing tests, read <span>`_docs/testing-guidelines.md`</span>
<span>-</span> For anything touching the UI, read <span>`_docs/design-system.md`</span></code></pre><p>The agent reads <code>AGENTS.md</code> at the start of every session, so it knows where to find the process, testing, and design rules if it needs them.</p><p>This way, it will load the design system only for a UI task and the testing guidelines only for a testing task. By loading each document only when it&#8217;s relevant, we keep <code>AGENTS.md</code> short while we continue adding written context to the project.</p><p>These documents are living documents, and I update them often. If I need to correct an agent during a coding session, I can ask it to modify the documents. Next time, it knows what I need, so I don&#8217;t have to correct it again.</p><p>You can use a prompt like this:</p><pre><code><code>Based on the corrections I made, find the relevant documents and update them.
Commit the current work before changing the documents.</code></code></pre><div class="callout-block" data-callout="true"><p>This is the first article in a new series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.</p><p>This year, I&#8217;m publishing the course notes as standalone articles, with one article for each module. Subscribe to receive the next article in the series.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/subscribe?"><span>Subscribe now</span></a></p></div><h2>Bootstrap the first task</h2><p>With <code>AGENTS.md</code> and <code>process.md</code> in place, we can start a new session and ask the agent to implement the first task:</p><pre><code><code>Implement task 1.</code></code></pre><p>For this project, the agent creates the Django app, dependencies, and a passing test.</p><h2>Grooming: the product manager agent</h2><p>We have a backlog of tasks, but they&#8217;re not precise enough.</p><p>We discussed this problem already: if the task isn&#8217;t specific, the agent will fill in the gaps during implementation. We risk spending time and tokens on something we don&#8217;t need.</p><p>Instead, we should ask the AI assistant to fill these gaps before writing any code. Then we review the specification, correct it, and give it to the coding agent to implement.</p><p>This process is called &#8220;grooming&#8221;: we groom a task to make it more specific. Then an engineer can implement it without asking a single question.</p><p>In real teams, product managers usually do this work. Here we&#8217;ll create a team of agents, and the first role we&#8217;ll define will be a PM.</p><p>Create a document:</p><pre><code><code>_docs/team/
  pm.md
</code></code></pre><p>Inside, write the description for the product manager agent:</p><pre><code>You&#8217;re a Product Manager

You groom a task before anyone implements it.

<span>-</span> Read the issue as written
<span>-</span> Rewrite it using the template in <span>`_docs/task-template.md`</span>
<span>-</span> Make the acceptance criteria checkable - someone should be able to
  point at the screen and say yes or no
<span>-</span> Think about the edge cases the person who filed it did not consider
<span>-</span> Do not write any code

Definition of done:

<span>-</span> The issue has all four sections filled in
<span>-</span> Every acceptance criterion can be checked by looking at the result
<span>-</span> Everything moved out of scope links to a follow-up issue
<span>-</span> An engineer who has never spoken to you could implement it from the
  issue and the documents it links

If something does not belong in this task, do not silently drop it.
File a follow-up issue and list it under out of scope with a link to
that issue, so it is clear what was moved and where it went.</code></pre><p>A groomed task has four sections:</p><ol><li><p>Goal - one or two sentences on what should be true afterwards.</p></li><li><p>Acceptance criteria - checkable statements.</p></li><li><p>Out of scope - what this change must not do.</p></li><li><p>Constraints - files it should stay inside, libraries it should or shouldn&#8217;t use, prior decisions it has to follow.</p></li></ol><p>We save the issue template as <code>_docs/task-template.md</code>:</p><pre><code><strong><span>## Goal</span></strong>

One or two sentences on what should be true when this is done.

<strong><span>## Acceptance criteria</span></strong>

<span>-</span> <span>[ ]</span> A statement you can check by looking at the result
<span>-</span> <span>[ ]</span> One line per case, including the awkward ones

<strong><span>## Out of scope</span></strong>

<span>-</span> Something that does not belong in this task, moved to #TASK-NUMBER

<strong><span>## Constraints</span></strong>

<span>-</span> Files this should stay inside
<span>-</span> Libraries to use
<span>-</span> Guidelines to follow</code></pre><p>We&#8217;ll need to groom every task, so we&#8217;ll add it to <code>process.md</code>:</p><pre><code>Roles

<span>-</span> PM - grooms a task before anyone implements it, follows <span>_</span>docs/team/pm.md</code></pre><p>We can now start a new session and ask the agent to groom an issue:</p><pre><code><code>Groom issue #4</code></code></pre><p>After it finishes, review the result.</p><p>We can catch a misunderstanding most cheaply while grooming: the issue is a paragraph, and correcting it costs one sentence. If we catch the same misunderstanding after implementation, we need a rewrite.</p><h2>Loop engineering</h2><p>After grooming one issue, we can ask the agent to groom the rest:</p><pre><code><code>Groom all GitHub issues. Process one issue at a time.</code></code></pre><p>This will mostly work, but the agent may eventually stop. It might say, &#8220;I&#8217;ve groomed issues 1, 2, and 3. Do you want me to proceed?&#8221;</p><p>The answer is almost always &#8220;yes&#8221;, but the agent has stopped and is waiting for us to say that explicitly. In many cases, I want the agent to continue automatically.</p><p>To do it, we can give the agent a goal:</p><pre><code><code>/goal groom all issues</code></code></pre><p>The <code>/goal</code> command will prompt the agent to continue, so we won&#8217;t need to do it manually. Instead, we delegate that responsibility to the harness: the system around an agent, such as Claude Code or Codex. When the agent stops, the harness checks whether the condition has been met. If it hasn&#8217;t, the harness resumes the work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E16L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E16L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 424w, https://substackcdn.com/image/fetch/$s_!E16L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 848w, https://substackcdn.com/image/fetch/$s_!E16L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 1272w, https://substackcdn.com/image/fetch/$s_!E16L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E16L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png" width="1456" height="649" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:649,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The goal prompts the agent to continue&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The goal prompts the agent to continue" title="The goal prompts the agent to continue" srcset="https://substackcdn.com/image/fetch/$s_!E16L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 424w, https://substackcdn.com/image/fetch/$s_!E16L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 848w, https://substackcdn.com/image/fetch/$s_!E16L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 1272w, https://substackcdn.com/image/fetch/$s_!E16L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe542ff1c-b76c-4277-83a9-e284e952656f_1588x708.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This approach is called &#8220;loop engineering&#8221;. It&#8217;s similar to a <code>while</code> loop: we repeat the work until a condition is met.</p><p>With loop engineering, the system runs a coding agent repeatedly instead of having us drive it manually, prompt by prompt.</p><p>There are multiple &#8220;engineering&#8221; levels when we work with coding agents:</p><ul><li><p>Prompt engineering - what we say when we interact with the agent</p></li><li><p>Context engineering - what the agent knows before it starts and what it can get during the session</p></li><li><p>Loop engineering - when it stops working</p></li><li><p>Graph engineering - who does what when there&#8217;s more than one agent (we&#8217;ll discuss it later)</p></li></ul><p>In June 2026, Addy Osmani published the <a href="https://addyo.substack.com/p/loop-engineering">Loop Engineering essay</a> that gave it a name, and Peter Steinberger compressed the idea into one tweet:</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/steipete/status/2063697162748260627&quot;,&quot;full_text&quot;:&quot;Here&#8217;s your monthly reminder that you shouldn&#8217;t be prompting coding agents anymore.\n\nYou should be designing loops that prompt your agents.&quot;,&quot;username&quot;:&quot;steipete&quot;,&quot;name&quot;:&quot;Peter Steinberger &#129438;&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/1131851609774985216/OcsssQ9J_normal.png&quot;,&quot;date&quot;:&quot;2026-06-07T18:58:39.000Z&quot;,&quot;photos&quot;:[],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:1803,&quot;retweet_count&quot;:1412,&quot;like_count&quot;:19860,&quot;impression_count&quot;:8491210,&quot;expanded_url&quot;:null,&quot;video_url&quot;:null,&quot;video_preview_media_key&quot;:null,&quot;belowTheFold&quot;:true}" data-component-name="Twitter2ToDOM"></div><p>A loop needs a stop condition: a checkable statement that tells the harness when to stop.</p><p>For <code>/goal groom all issues</code>, the stop condition is &#8220;all issues are groomed&#8221;. After each agent run, the harness checks whether that condition is true and resumes the agent if it isn&#8217;t.</p><p>The stop condition must be something the model can evaluate. &#8220;All issues are groomed&#8221;, &#8220;all tests pass&#8221;, and &#8220;no file is over 200 lines&#8221; are checkable, but &#8220;make the code better&#8221; isn&#8217;t. If the stop condition isn&#8217;t checkable, the agent can stop too early or run forever.</p><p>Claude Code and Codex provide the <code>/goal</code> loop by default. If your harness doesn&#8217;t provide it, you can implement it yourself using stop hooks.</p><h2>Implementation: the software engineer agent</h2><p>After grooming the issue, we can give it to a software engineer, the agent who will write the code.</p><p>Define the second role:</p><pre><code><code>_docs/team/
  software-engineer.md</code></code></pre><p>Put this definition inside:</p><pre><code>You&#8217;re a Software Engineer

You implement one groomed task at a time.

<span>-</span> Read the issue and implement what it describes
<span>-</span> Implement against the acceptance criteria, do not change them
<span>-</span> Stay inside the files and constraints the issue names
<span>-</span> Write tests for what you built
<span>-</span> Do not close the issue
<span>-</span> Commit regularly

Definition of done:

<span>-</span> Every acceptance criterion in the issue is implemented
<span>-</span> Tests are written for the new behaviour, and the whole suite passes
<span>-</span> The work is committed
<span>-</span> The issue is still open, with a comment saying what you did

If an acceptance criterion is wrong, impossible, or contradicts
another one, create a comment on the issue about it.</code></pre><p>Add one more line to <code>process.md</code>:</p><pre><code>Roles

<span>-</span> PM - grooms a task before anyone implements it, follows <span>_</span>docs/team/pm.md
<span>-</span> Engineer - implements one groomed task, follows <span>_</span>docs/team/software-engineer.md</code></pre><p>Then ask the agent to implement a task in a fresh session:</p><pre><code><code>Implement issue #2</code></code></pre><p>The engineer stops when the code is written and its own tests pass. It&#8217;s still too early to say that the task is properly implemented, so we need to test it.</p><h2>Testing: the QA engineer agent</h2><p>When the same agent writes and judges the code, it&#8217;s grading its own homework.</p><p>If we ask, &#8220;Is this correct?&#8221; we&#8217;ll get a definite &#8220;yes&#8221;, but the agent might have missed many edge cases.</p><p>In the real world, we ask other people to validate our work. We have code reviews, and many teams have designated QA engineers whose focus is making sure the code is reliable.</p><p>That&#8217;s why we&#8217;ll also add a QA engineer to our team.</p><p>Add the third role:</p><pre><code><code>_docs/team/
  qa-engineer.md</code></code></pre><p>The description:</p><pre><code>You&#8217;re a QA Engineer

You check finished work against the issue that specified it.

<span>-</span> Read the acceptance criteria from the issue
<span>-</span> Check each one against what the code actually does
<span>-</span> Run the tests, and say which ones you ran
<span>-</span> Look for the cases the criteria describe but the tests do not cover
<span>-</span> Do not fix anything you find. Report it by creating a comment

Your output is a verdict: PASS or FAIL. It is FAIL if a single
acceptance criterion fails. Post it as a comment on the issue:

<strong><span>## QA: FAIL</span></strong>

<span>-</span> <span>[x]</span> A visitor can create an account with a username and password - PASS
<span>-</span> <span>[ ]</span> A duplicate username shows a visible error - FAIL
      Submitted an existing username and received an unhandled error

Tests: <span>`uv run pytest`</span>, 18 passed, 0 failed

Definition of done:

<span>-</span> The comment starts with PASS or FAIL
<span>-</span> Every acceptance criterion has a verdict against it
<span>-</span> Every FAIL says what you did and what happened
<span>-</span> The test command and its result are included
<span>-</span> Nothing in the code was changed

Ignore what the implementation says it does. Only the acceptance
criteria and the running code count.</code></pre><p>Note: you may need to adjust this role for your project if you don&#8217;t use <code>uv</code> and <code>pytest</code>.</p><p>And the last line in <code>process.md</code>:</p><pre><code>Roles

<span>-</span> PM - grooms a task before anyone implements it, follows <span>_</span>docs/team/pm.md
<span>-</span> Engineer - implements one groomed task, follows <span>_</span>docs/team/software-engineer.md
<span>-</span> QA - checks the result against the acceptance criteria, follows <span>_</span>docs/team/qa-engineer.md</code></pre><p>Then, in a new session, ask:</p><pre><code><code>Test issue #2</code></code></pre><p>If we get a <code>PASS</code>, that&#8217;s great. If we get a <code>FAIL</code>, that&#8217;s also useful: we caught a regression. So we start a new engineer session, use the QA comment as input, and ask the engineer to fix it.</p><p>We iterate until QA says <code>PASS</code>.</p><h2>Graph engineering</h2><p>We have three roles:</p><ul><li><p>Product manager grooms an issue</p></li><li><p>Software engineer implements it</p></li><li><p>QA engineer tests it, outputs <code>PASS</code> or <code>FAIL</code></p></li></ul><p>If QA says <code>FAIL</code>, the engineer will need to reimplement it. Otherwise, the task is done.</p><p>We can visualize this process as a graph:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lxKR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lxKR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 424w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 848w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 1272w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lxKR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png" width="1440" height="360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/525257ec-6465-4bad-a749-099a4557eba0_1440x360.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:360,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;01-agent-workflow-styled.png&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="01-agent-workflow-styled.png" title="01-agent-workflow-styled.png" srcset="https://substackcdn.com/image/fetch/$s_!lxKR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 424w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 848w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 1272w, https://substackcdn.com/image/fetch/$s_!lxKR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F525257ec-6465-4bad-a749-099a4557eba0_1440x360.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>If we have an orchestrator that launches these agents automatically instead of us doing it manually, we get &#8220;graph engineering&#8221;. We define a graph with specialized agents as nodes and describe how the work moves from one to another.</p><p>The earliest explicit use of the term I found in the article from Josh C. Simmons (July 4, 2026): <a href="https://www.drjoshcsimmons.com/writing/we-are-entering-the-graph-engineering-phase">The Graph Engineering phase</a>.</p><p>Peter Steinberger sparked a wider discussion in a <a href="https://x.com/steipete/status/2078277297791189132">July 18 post</a> by asking whether the conversation had shifted from loops to graphs. A few hours later, Hamel Husain used <a href="https://x.com/HamelHusain/article/2078346425621237935">&#8220;graph engineering&#8221;</a> in an article headline.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zzut!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zzut!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 424w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 848w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 1272w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zzut!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png" width="526" height="324.99097291875626" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:997,&quot;resizeWidth&quot;:526,&quot;bytes&quot;:370439,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/208017204?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Zzut!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 424w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 848w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 1272w, https://substackcdn.com/image/fetch/$s_!Zzut!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8caa78d3-a47c-42ca-9a55-e042601bcc24_997x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Hamel put just a GIF saying &#8220;stop it&#8221; in the article, but people liked the term and started using it for multi-agent orchestration.</p><p>I wouldn&#8217;t call it a new idea, though, and loop engineering is definitely not dead. Even with graphs, we still need a loop to drive the work and orchestrate it across multiple agents with different roles.</p><h2>The orchestrator</h2><p>To implement it, we need an orchestrator.</p><p>Describe it in <code>process.md</code>:</p><pre><code>Orchestrator

The main session is the orchestrator. It launches the PM, the engineer
and QA as subagents. It does not groom, implement or test itself.

Lifecycle

<span>1.</span> Pick the next open issue from the backlog
<span>2.</span> PM grooms it
<span>3.</span> Engineer implements it
<span>4.</span> QA verifies it
<span>5.</span> On FAIL, back to step 3 with the QA comment as input
<span>6.</span> On PASS, close the issue
<span>7.</span> Repeat until the backlog is empty

Rules

<span>-</span> Do not skip step 2
<span>-</span> The engineer does not close the issue
<span>-</span> QA does not fix the code, only outputs PASS or FAIL
<span>-</span> The orchestrator closes the issue only after QA outputs PASS</code></pre><p>We can now start a fresh session and launch the loop:</p><pre><code><code>/goal work through the backlog</code></code></pre><p>The agent reads <code>AGENTS.md</code>, finds <code>process.md</code>, follows the lifecycle, and dispatches the agents according to the documents in <code>_docs/team/</code>.</p><p>Because we combine it with a goal, the orchestrator runs until the condition holds.</p><p>You can see the result in <a href="https://github.com/alexeygrigorev/retroloop">retroloop</a>.</p><p>Note: this approach takes significantly more time and tokens than a direct loop with only a software engineer. In many cases, you don&#8217;t need this complexity. Often, a simple prompt or loop is enough.</p><p>I&#8217;ve followed this approach on many projects, including the <a href="https://aishippinglabs.com/">AI Shipping Labs website</a>.</p><p>I describe it in <a href="https://alexeyondata.substack.com/p/i-built-an-ai-agent-team-for-software">I Built an AI Agent Team for Software Development</a>. Here, I distill that article into a tutorial.</p><h2>Next in the series</h2><p>We have now finished the first module. It&#8217;s theoretical, but the next modules are very practical. </p><p>We will implement an application from the specification to making it production-ready in 3 steps:</p><ul><li><p>Build: <a href="https://alexeyondata.substack.com/p/build-and-ship-a-full-stack-app-with">Build and Ship a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Deploy: <a href="https://alexeyondata.substack.com/p/deploy-a-full-stack-app-with-ai-coding">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Operate: <a href="https://aishippingblog.com/p/devops-and-observability-for-an-ai">DevOps and Observability for an AI-Built App</a></p></li></ul><div class="callout-block" data-callout="true"><p style="text-align: center;">Subscribe to receive the next article in the series.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/subscribe?"><span>Subscribe now</span></a></p></div>]]></content:encoded></item><item><title><![CDATA[How to Write a Good README]]></title><description><![CDATA[16 sections your project should have in the README]]></description><link>https://aishippingblog.com/p/how-to-write-a-good-readme</link><guid isPermaLink="false">https://aishippingblog.com/p/how-to-write-a-good-readme</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Thu, 16 Jul 2026 19:40:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Lao2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>Your README is the first file people read in your project, and sometimes the only one.</span></p><p><span>Think of it as the project&#8217;s landing page. It should help someone quickly understand what you built, why it matters, and whether they should explore it further. This applies to pet projects, course submissions, and portfolio projects you create to support a job application.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>At my previous job, I was heavily involved in hiring. I conducted the first technical screening after the recruiter call, which meant reviewing candidates&#8217; profiles and projects. I would open a repository, scan the README, and decide within about ten seconds whether to continue. Most of the time, I wouldn&#8217;t continue. If the README didn&#8217;t tell me what the project was about or how to run it, I wouldn&#8217;t spend time trying to figure that out myself.</span></p><p><span>I now see the same problem in the courses I run. Each cohort produces hundreds of student projects, and all of my courses use peer review, in which participants assess one another&#8217;s work against a set of criteria.</span></p><p><span>Reviewers need to understand the problem, inspect the implementation, and determine which criteria the project satisfies. A README that makes this difficult can cost the author course points. I have seen projects lose points because important work, such as evaluation results, was buried several notebooks deep and never mentioned in the README.</span></p><p>In this article, I'll explain how your README should look like, which sections it should contain, and who you should write for, so your project gets the attention it deserves.</p><p><span>It's an extended version of the material I prepared earlier this year to the students of </span>my <a href="https://maven.com/alexey-grigorev/from-rag-to-agents">AI Engineering Buildcamp</a> course. Right now the partici<span>pants of </span><a href="https://github.com/DataTalksClub/llm-zoomcamp"><span>LLM Zoomcamp</span></a><span> will find it especially useful, but the same principles apply to the </span><a href="https://datatalks.club/blog/guide-to-free-online-courses-at-datatalks-club.html"><span>other Zoomcamps</span></a><span> and to most engineering projects.</span></p><p><span>Throughout the article, I will use one running example: </span><a href="https://github.com/alexeygrigorev/fitness-assistant"><span>Fitness Assistant</span></a><span>, a RAG application that helps users find exercises and suitable alternatives. I built it two years ago as a reference project for LLM Zoomcamp. The repository was already in reasonable shape, but I refined its README further while preparing this article.</span></p><h2><span>Start with the reader</span></h2><p><span>Before you write a README, think about the person who is going to read it.</span></p><p><span>I suggest writing for three audiences at once: peer reviewers, hiring teams, and your future self.</span></p><h3><span>Peer reviewers</span></h3><p><span>The first audience is someone assessing your project against a list of criteria.</span></p><p><span>In the Zoomcamps I run, participants review one another&#8217;s projects and decide how many points each submission earns. They need to find evidence for the problem description, evaluation, monitoring, reproducibility, and other criteria.</span></p><p><span>Even when your project is not part of a course, it helps to imagine that it will be reviewed this way.</span></p><p><span>An imaginary peer reviewer gives you a useful standard. Assume that someone will inspect the repository carefully and ask whether you have covered the important parts of the project. Your README should help them find the evidence without searching through every file and notebook.</span></p><h3><span>Hiring teams</span></h3><p><span>The second audience is the hiring team.</span></p><p><span>A recruiter, hiring manager, or engineer may not spend as much time on the initial review. They first want to understand what the project is and whether it is relevant to the role. Only then will they decide whether to inspect the implementation, evaluation, tests, or architecture in more detail.</span></p><p><span>This is why optimizing for a peer reviewer is still useful. The README should be easy to scan, but it should also contain enough detail for a technical reader who decides to go deeper.</span></p><h3><span>Your future self</span></h3><p><span>The third audience is your future self.</span></p><p><span>After several months, you may return to the repository and no longer remember how the project works, how to run it, why you made certain decisions, or what you planned to improve.</span></p><p><span>A good README should give you enough context to understand the project without reconstructing it from the code and commit history.</span></p><h3><span>Write for all three</span></h3><p><span>These audiences are different, but their needs are compatible.</span></p><p><span>The hiring team needs a clear overview and a reason to continue. The peer reviewer needs evidence and detail. Your future self needs enough context to understand and continue the work.</span></p><p><span>The strongest README supports all three: it is easy to scan at first, detailed enough to inspect, and complete enough to return to later.</span></p><h2><span>What does the reader need to understand?</span></h2><p><span>Most readers approach a project with four broad questions:</span></p><ol><li><p><span>What is this project?</span></p></li><li><p><span>Does it work well?</span></p></li><li><p><span>Can I run and reproduce it?</span></p></li><li><p><span>How was it built?</span></p></li></ol><p><span>Let&#8217;s start with the first question.</span></p><h2><span>Part 1: What is this project?</span></h2><p><span>Readers need to understand what you built.</span></p><p><span>The beginning of the README should answer three questions:</span></p><ul><li><p><span>Who is the project for?</span></p></li><li><p><span>What problem does it solve?</span></p></li><li><p><span>What did you build to solve it?</span></p></li></ul><p><span>Use the title and short description to provide the immediate answer. Then explain the problem in more detail and show how the project works.</span></p><h3><span>1. Title and description</span></h3><p><span>Start with a clear project title and one or two sentences that describe the user, the problem, and the solution.</span></p><p><span>Avoid opening with a list of technologies. This tells the reader which tools you used, but not what the application does or why anyone would use it.</span></p><p><span>For Fitness Assistant, I </span><a href="https://github.com/alexeygrigorev/fitness-assistant#fitness-assistant"><span>use this description</span></a><span>:</span></p><div class="callout-block" data-callout="true"><p><span>A conversational AI that helps users choose exercises and find alternatives. It makes fitness more approachable for beginners who find gyms intimidating or cannot always access a personal trainer.</span></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lao2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lao2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 424w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 848w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 1272w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lao2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png" width="1456" height="799" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:799,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Lao2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 424w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 848w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 1272w, https://substackcdn.com/image/fetch/$s_!Lao2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F956ba912-9f3c-4017-9ef9-523280df5d31_1768x970.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>In two sentences, the reader learns:</span></p><ul><li><p><span>The user: fitness beginners without regular access to a trainer</span></p></li><li><p><span>The problem: choosing exercises and finding alternatives can be difficult</span></p></li><li><p><span>The solution: a conversational AI assistant that provides guidance</span></p></li></ul><p><span>The exact formula will vary by project, but a useful starting point is:</span></p><div class="callout-block" data-callout="true"><p><span>[Project name] is a [type of system] that helps [specific user] do [task or achieve an outcome].</span></p></div><p><span>You can add a second sentence to explain the main obstacle or why the project matters.</span></p><p><span>For example:</span></p><div class="callout-block" data-callout="true"><p><span>A support assistant that helps DataTalks.Club students find answers across course materials. It retrieves relevant information from the course knowledge base instead of requiring students to search through videos, documents, and Slack discussions manually.</span></p></div><p><span>Keep the description concrete. Phrases such as &#8220;AI-powered platform,&#8221; &#8220;innovative solution,&#8221; and &#8220;intelligent system&#8221; add little unless you explain what the system actually does.</span></p><p><span>If you struggle to write the description, generate several alternatives and compare them. You can ask an AI assistant to analyze the repository or to describe the project based on your notes. However, you still need to choose the version that accurately represents the project and makes sense to your audience.</span></p><h3><span>2. Problem</span></h3><p><span>The short description introduces the problem, and the problem section explains it.</span></p><p><span>Here&#8217;s a simple structure that works well:</span></p><ol><li><p><span>What is the user trying to do?</span></p></li><li><p><span>What makes that difficult?</span></p></li><li><p><span>Why are the existing options insufficient?</span></p></li><li><p><span>Who experiences this problem?</span></p></li></ol><p><span>For my Fitness Assistant project, the problem is </span><a href="https://github.com/alexeygrigorev/fitness-assistant#problem"><span>described like that</span></a><span>:</span></p><ul><li><p><span>Beginners often struggle to choose appropriate exercises</span></p></li><li><p><span>Gyms can feel intimidating when someone does not know how to use the equipment</span></p></li><li><p><span>An exercise may be unsuitable because of an injury, missing equipment, or personal preference</span></p></li><li><p><span>Personal trainers can provide guidance, but they are not always available or affordable</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hsgM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hsgM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 424w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 848w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 1272w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hsgM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png" width="1456" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hsgM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 424w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 848w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 1272w, https://substackcdn.com/image/fetch/$s_!hsgM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd541fdf2-d6d0-4cff-b5c8-dd130d7c057c_1798x766.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Keep this section proportional to the project. You don&#8217;t need a long market analysis for a small course project. A few specific paragraphs are usually enough to establish why the application exists and who benefits from it.</span></p><h3><span>3. Demo</span></h3><p><span>After you explain the project, show it working.</span></p><p><span>A reader should not need to install dependencies, create API keys, and start several services just to see what the application does. Many readers will decide whether to continue before they run anything locally.</span></p><p><span>You can demonstrate the project with:</span></p><ul><li><p><span>Live application</span></p></li><li><p><span>Short video</span></p></li><li><p><span>GIF</span></p></li><li><p><span>Screenshots</span></p></li><li><p><span>Sample input and output</span></p></li></ul><p><span>Use the format that best represents the project. A screenshot may be enough for a dashboard or static interface. A short video is usually more useful when the application involves several steps, dynamic output, or interaction with an AI assistant.</span></p><p><span>For an AI application, the demo might show:</span></p><ol><li><p><span>User entering a realistic request</span></p></li><li><p><span>System retrieving or processing the relevant information</span></p></li><li><p><span>Application producing an answer</span></p></li><li><p><span>User providing feedback or asking a follow-up question</span></p></li></ol><p><span>Keep the main demonstration focused. Show the core workflow first rather than spending several minutes explaining the repository structure or installation process.</span></p><p><span>You can cover evaluation results, monitoring dashboards, and architecture later in the video, but a reader should see the application working within the first few moments.</span></p><p><span>If the project is deployed, place the live link near the top of the README. A reader can then try the application without setting it up locally. Keep the video or screenshots as well because the hosted version may later become unavailable or require credentials.</span></p><p><span>For Fitness Assistant, I </span><a href="https://www.youtube.com/watch?v=RiQcSHzR8_E"><span>recorded a video</span></a><span> that shows the conversation flow and the main application features.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RzBG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RzBG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 424w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 848w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 1272w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RzBG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png" width="1456" height="755" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/505ed47b-aab0-4588-89f5-34251087676d_1798x932.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:755,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RzBG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 424w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 848w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 1272w, https://substackcdn.com/image/fetch/$s_!RzBG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F505ed47b-aab0-4588-89f5-34251087676d_1798x932.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>Part 2: Does it work well?</span></h2><p><span>For an AI engineering project, readers need three kinds of evidence:</span></p><ul><li><p><span>Evaluation: How well does the AI system perform?</span></p></li><li><p><span>Testing: Does the software behave as expected?</span></p></li><li><p><span>Monitoring: What happens when the application processes real requests?</span></p></li></ul><h3><span>1. Evaluation</span></h3><p><span>Without evaluation, readers can see that the application generates answers, but they cannot judge how consistently or accurately it performs.</span></p><p><span>Describe the evaluation dataset and the kinds of cases it includes. The dataset should reflect the situations in which you expect the application to operate. A system evaluated only on simple, carefully written examples may perform poorly when users phrase the same requests differently.</span></p><p><span>Also explain what you changed after establishing the baseline. The result is more useful when the reader can follow the progression from the initial approach to the final one.</span></p><p><span>For my Fitness Assistant, the </span><a href="https://github.com/alexeygrigorev/fitness-assistant#evaluation"><span>README reports</span></a><span> both retrieval and end-to-end RAG evaluation. After adding field boosting, retrieval reached a 94% hit rate and 90% mean reciprocal rank. The final RAG flow produced answers classified as relevant in 83% of cases when using gpt-4o-mini. The README also includes the parameters that produced the best retrieval results and links to the evaluation notebooks and datasets.</span></p><p><span>Keep the summary in the README, but store the detailed code and results in a separate, clearly named location. Document how to rerun the evaluation so the reader can verify the results or test a different approach.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cOgW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cOgW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 424w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 848w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 1272w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cOgW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png" width="1456" height="645" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:645,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cOgW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 424w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 848w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 1272w, https://substackcdn.com/image/fetch/$s_!cOgW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02238cb2-3151-4c2e-ae8b-50c378bbdc98_1784x790.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>2. Testing</span></h3><p><span>Explain which tests the project includes and how to run them with one command (if possible).</span></p><p><span>When the project requires additional services or configuration, state that before the command. A reader should not have to discover that the tests require a database, an API key, or a running container from the resulting error messages.</span></p><p><span>If the project does not have automated tests, say so. My Fitness Assistant currently has no automated tests. I </span><a href="https://github.com/alexeygrigorev/fitness-assistant#testing"><span>list this as a limitation</span></a><span>.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3SK-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3SK-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 424w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 848w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 1272w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3SK-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png" width="1456" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:798,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3SK-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 424w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 848w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 1272w, https://substackcdn.com/image/fetch/$s_!3SK-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a0d9c3c-4119-4ec5-87e4-ddf5cbeb7460_1788x980.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>This tells the reader that I understand what is missing and prevents the README from suggesting that the project is more complete than it is. Honesty is more useful than vague claims when the repository contains no evidence to support them.</span></p><h3><span>3. Monitoring</span></h3><p><span>Evaluation tells you how the system performed on a prepared dataset. Monitoring helps you understand what happens after people begin using it.</span></p><p><span>In the monitoring section, describe the metrics your system captures and where it stores them. Explain how a reader can view this data, which specific events or metrics you prioritize, and how these observations enable you to diagnose and resolve issues. If you have a dashboard, include a screenshot and explain what its panels show.</span></p><p><span>For Fitness Assistant, the application stores conversations in PostgreSQL and displays monitoring data in Grafana. The </span><a href="https://github.com/alexeygrigorev/fitness-assistant#monitoring"><span>README includes a screenshot and explains the dashboard panels</span></a><span>, which track:</span></p><ul><li><p><span>5 most recent conversations</span></p></li><li><p><span>Positive and negative user feedback</span></p></li><li><p><span>OpenAI API cost</span></p></li><li><p><span>Token usage</span></p></li><li><p><span>Model used</span></p></li><li><p><span>Response time</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kwqz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kwqz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 424w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 848w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 1272w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kwqz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png" width="1456" height="1169" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1169,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kwqz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 424w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 848w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 1272w, https://substackcdn.com/image/fetch/$s_!kwqz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5621a924-dd68-418a-a18e-af9f8964a464_1796x1442.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Part 3: Can I run and reproduce it?</h2><p><span>After readers understand what the project does and how well it performs, they may want to run it themselves.</span></p><h3><span>1. Quickstart</span></h3><p><span>The </span><a href="https://github.com/alexeygrigorev/fitness-assistant#quickstart"><span>quickstart</span></a><span> should provide the shortest reliable path from a clean machine to a running application.</span></p><p><span>Include </span><a href="https://github.com/alexeygrigorev/fitness-assistant#prerequisites"><span>prerequisites</span></a><span>, the repository clone command, dependency installation, environment configuration, required services, and the command that starts the application.</span></p><p><span>For example:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;3eafd71b-c187-4fe6-a003-a3d085dd23b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">git clone https://github.com/username/project-name.git
cd project-name
uv sync
cp .env.example .env
docker compose up</code></pre></div><p><span>It will also help to mention some core tools and technologies you used:</span></p><ul><li><p><span>Python version</span></p></li><li><p><span>Package manager, such as uv, Poetry, or pip</span></p></li><li><p><span>Docker and Docker Compose</span></p></li><li><p><span>Node.js version, if the project includes a frontend</span></p></li><li><p><span>Database or system dependencies that do not run in containers</span></p></li></ul><p><span>If the project supports several setup methods, show the recommended one first.</span></p><p>In addition to quickstart, also include:</p><ul><li><p><span>Local development: detailed instructions for running services separately</span></p></li><li><p><span>Troubleshooting: solutions to common setup problems</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4JW7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4JW7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 424w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 848w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 1272w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4JW7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png" width="1456" height="747" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:747,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4JW7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 424w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 848w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 1272w, https://substackcdn.com/image/fetch/$s_!4JW7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11661c42-9e51-4956-bd6a-4facd187c4a9_1790x918.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>2. Data and configuration</span></h3><p><span>Many projects fail to run because the code is available, but the required data or configuration is not.</span></p><p><span>Document every external input the reader needs. Name the source, file, destination, and command when possible:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;a636996a-2c87-4d98-bb56-e38604a56f60&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">python scripts/download_data.py</code></pre></div><p><span>For configuration, list the required environment variables and explain what each one controls.</span></p><p><span>Provide an example file such as </span><code>.env.example</code><span>:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;d06b7f61-4355-4b66-99fa-2269adad924d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">OPENAI_API_KEY=
POSTGRES_HOST=postgres
POSTGRES_DB=fitness
POSTGRES_USER=fitness
POSTGRES_PASSWORD=</code></pre></div><p><span>Also, clearly state which settings are required and which are optional ones. A reader should know whether the application can start without a monitoring service, cloud account, or third-party API.</span></p><p><span>For Fitness Assistant, the README contains a description of a dataset that I used for creating the assistant.</span></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CXW2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CXW2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 424w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 848w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 1272w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CXW2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png" width="1456" height="297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:297,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CXW2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 424w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 848w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 1272w, https://substackcdn.com/image/fetch/$s_!CXW2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37c7ab83-0f82-4dff-8377-34445c3bf708_1786x364.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3><span>3. Deployment</span></h3><p><span>If the project is deployed, place the live URL near the top of the README, immediately after the title and description.</span></p><p><span>The deployment section itself should explain where the application is hosted, which services it uses, how deployment works, which environment variables or secrets are required, whether deployment happens manually or through CI/CD, how someone else could deploy their own version</span></p><p><span>Depending on the project, you might deploy it with Streamlit Community Cloud, Render, Railway, a managed container platform, or a virtual machine running Docker.</span></p><p><span>For example:</span></p><p><span>The API runs on Render, PostgreSQL is hosted on Neon, and the frontend is deployed on Vercel. A GitHub Actions workflow deploys the API after tests pass on the main branch.</span></p><p><span>If deployment requires several services, include a short description of how they connect. Link to longer deployment instructions when the process would make the main README too long.</span></p><p><span>Fitness Assistant does not currently have a public deployment. The README states this and provides two alternatives</span></p><ol><li><p><span>Start the complete system with Docker Compose</span></p></li><li><p><span>Run the application and supporting services locally</span></p></li></ol><h2><span>Part 4: How was it built?</span></h2><p><span>Once readers understand what the project does, how well it performs, and how to run it, they may want to inspect the implementation in more detail.</span></p><h3><span>1. Architecture</span></h3><p><span>Use the architecture section to show how data and requests move through the system.</span></p><p><span>You can create the diagram with Mermaid, an image, or a simple text-based flow. The format matters less than whether the reader can understand the system without first reading the code.</span></p><p><span>For Fitness Assistant, the </span><a href="https://github.com/alexeygrigorev/fitness-assistant#architecture"><span>architecture diagram</span></a><span> shows how the main components interact:</span></p><ol><li><p><span>A user sends a request to the Flask application.</span></p></li><li><p><span>The application uses minsearch to retrieve relevant exercises.</span></p></li><li><p><span>It sends the request and retrieved context to OpenAI.</span></p></li><li><p><span>It returns the generated answer to the user.</span></p></li><li><p><span>It stores the conversation and usage information in PostgreSQL.</span></p></li><li><p><span>Grafana reads the stored data and displays it in a monitoring dashboard.</span></p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2DC7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2DC7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 424w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 848w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 1272w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2DC7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png" width="1456" height="1136" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1136,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2DC7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 424w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 848w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 1272w, https://substackcdn.com/image/fetch/$s_!2DC7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6aae8b02-5203-4e5b-a452-2858b87b8a41_1786x1394.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>It also helps to add a short explanation below the diagram. Diagrams show relationships well, but they do not always explain why a component exists or what responsibility it has.</span></p><h3><span>2. Project structure</span></h3><p><span>The project structure section helps readers find the code behind the claims you make elsewhere in the README.</span></p><p><span>Include a simplified file tree with one-line descriptions:</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eYAs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eYAs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 424w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 848w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eYAs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png" width="1456" height="862" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:862,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eYAs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 424w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 848w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!eYAs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dbf3d44-fbf8-4ace-a6a9-d2eae36493b6_1790x1060.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Do not paste the complete output of tree if the repository contains dozens of generated, configuration, or unimportant files. Show the paths that help the reader understand and navigate the project.</span></p><p><span>If you use notebooks, explain what each notebook contains. Names such as </span><code>notebook1.ipynb</code><span> or </span><code>final_v2.ipynb</code><span> give the reader little information. Prefer descriptive filenames, and state which notebook contains the reported results.</span></p><h3><span>3. Decisions and trade-offs</span></h3><p><span>The technologies you chose matter less than the reasoning behind them.</span></p><p><span>Describe the decisions that materially shaped the project, such as model selection, retrieval method, database choice, framework choice, etc.</span></p><p><span>A useful format is:</span></p><div class="callout-block" data-callout="true"><p><span>I chose X over Y because of constraint Z. The downside was A. I accepted it because B.</span></p></div><p><span>For example:</span></p><div class="callout-block" data-callout="true"><p><span>I used minsearch instead of a managed vector database because the dataset contains only 207 exercises and the project should be easy to run locally. This approach does not provide the scalability or indexing features of a production search service, but those features were unnecessary for this dataset.</span></p></div><p><span>Or:</span></p><div class="callout-block" data-callout="true"><p><span>I used gpt-4o-mini for the final RAG flow because it provided acceptable evaluation results at a lower cost than the larger model. The larger model performed better on some difficult cases, but the improvement did not justify the additional cost for this project.</span></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wjlt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wjlt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 424w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 848w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 1272w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wjlt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png" width="1456" height="477" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:477,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wjlt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 424w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 848w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 1272w, https://substackcdn.com/image/fetch/$s_!Wjlt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24c92304-901b-465c-9ab1-b713b2f844bd_1776x582.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><span>4. CI/CD</span></h3><p><span>The CI/CD section explains which checks and deployment steps run automatically.</span></p><p><span>At a minimum, document what triggers the workflow, which checks run, what happens when a check fails, and whether successful changes are deployed.</span></p><p><span>For example:</span></p><div class="callout-block" data-callout="true"><p><span>On every pull request, GitHub Actions installs the dependencies, runs the test suite, and checks code formatting. Changes cannot be merged until these checks pass.</span></p></div><p><span>AI evaluations can be slower and more expensive than conventional tests, so you may not want to run the full evaluation suite on every commit. You could run a smaller set on pull requests and the complete evaluation before a release.</span></p><p><span>If deployment is automated, describe the sequence. Link to the workflow files so readers can inspect the implementation.</span></p><p><span>If the project has no CI/CD, do not create a section that implies otherwise. You can mention its absence under limitations instead.</span></p><h2><span>Nice to have: What is the project scope and its limitations?</span></h2><p><span>A good README also helps readers understand where it stops.</span></p><p><span>Describing these boundaries shows that you can assess your own work realistically and distinguish between what you implemented, tested, and only considered.</span></p><h3><span>1. Limitations</span></h3><p><span>Use the limitations section to explain what the project does not currently handle well.</span></p><p><span>Be specific. Name the boundary and its consequence:</span></p><div class="callout-block" data-callout="true"><p><span>The knowledge base contains 207 exercises generated with ChatGPT. It covers common strength exercises but may omit less common movements or contain inaccurate descriptions.</span></p></div><p><span>Or:</span></p><div class="callout-block" data-callout="true"><p><span>The application does not authenticate users, so the current version is suitable for local demonstrations but not for a public deployment that stores personal data.</span></p></div><p><span>For Fitness Assistant, the </span><a href="https://github.com/alexeygrigorev/fitness-assistant#limitations"><span>limitations</span></a><span> include the absence of automated tests and a public deployment. The dataset is also relatively small and synthetic, so the application cannot provide complete or medically reliable exercise advice.</span></p><p><span>Do not hide a known weakness behind vague wording. At the same time, do not turn the section into an apology. State the boundary plainly and explain its practical effect.</span></p><h3><span>2. Future work</span></h3><p><span>Future work should follow from the limitations or from the evidence you gathered while building and evaluating the project.</span></p><p><span>Keep the list realistic and prioritized. Explain what you would change and why:</span></p><div class="callout-block" data-callout="true"><p><span>Add evaluation cases for requests involving injuries and equipment limitations because the current dataset contains too few examples of these scenarios.</span></p></div><h3><span>3. Self-evaluation</span></h3><p><span>For a course project, you may also include a self-evaluation against the project rubric.</span></p><p><span>Go through each criterion, state the score you believe the project earns, and point to the evidence that supports it.</span></p><p><span>This helps the reviewer navigate the repository and shows that you assessed the project against the same criteria they will use.</span></p><p><span>Do not award points without evidence or treat the self-evaluation as the final score. The reviewer still needs to verify your claims and make an independent judgment.</span></p><p><span>A self-evaluation can also reveal gaps before submission. If you cannot point to evidence for a criterion, either improve the project or adjust the score you claim.</span></p><p><span>You can use an AI assistant to perform an initial review of the repository against the rubric. Ask it to identify the evidence for each criterion and flag anything it cannot verify. Then check every conclusion yourself because the assistant may overlook files, misunderstand the implementation, or give credit for features that are only mentioned but not implemented.</span></p><h2><span>Common mistakes</span></h2><p><span>I see the same README problems across course projects in almost every cohort:</span></p><ul><li><p><span>Missing setup instructions. If I cannot run the project from a clean clone, it loses points on reproducibility.</span></p></li><li><p><span>No evaluation. An AI application without evaluation is only a demo.</span></p></li><li><p><span>No demo. If readers have to run the code before they can see what it does, many will not.</span></p></li><li><p><span>Important work is buried in notebooks. Link to the evaluation, prompts, experiments, and results from the README.</span></p></li><li><p><span>A stale README. The commands, screenshots, metrics, and deployment links should match the project&#8217;s current version.</span></p></li><li><p><span>A wall of text. Use clear sections, screenshots, diagrams, commands, and concrete results to make the README easy to scan.</span></p></li><li><p><span>Planned work is described as implemented. Use the present tense only for features that already exist. Put unfinished ideas under limitations or future work.</span></p></li><li><p><span>A README written as an afterthought. A list of technologies and a few setup commands is not enough. Explain the problem, show the project, and provide evidence that it works.</span></p></li></ul><h2><span>Using AI to write and review the README</span></h2><p><span>A coding agent can inspect your repository and help draft the README, but do not publish the first version it generates.</span></p><p><span>Start by asking it to identify the main application flow, setup commands, configuration, tests, evaluation, monitoring, and deployment files. Then use that information to draft one section at a time for a specific audience.</span></p><p><span>For example:</span></p><div class="callout-block" data-callout="true"><p><span>Draft three short project descriptions for an ML engineering hiring manager. Each should explain the user, problem, and solution without starting with the technology stack.</span></p></div><p><span>AI can also review the finished README against the repository or a course rubric. Ask it to flag unsupported claims, missing files, incorrect commands, undocumented environment variables, and criteria for which it cannot find evidence.</span></p><p><span>Do not force every section from this article into the README. Include only what is relevant to the project, and order the sections according to what your reader needs most.</span></p><p><span>Finally, verify everything yourself. Coding agents can invent commands, misunderstand the architecture, describe planned features as implemented, or report outdated results. Run every command and check every number, path, link, and claim before publishing.</span></p><h2><span>Good project examples</span></h2><p><span>Here are a few strong READMEs from </span><a href="https://maven.com/alexey-grigorev/from-rag-to-agents"><span>AI Engineering Buildcamp</span></a><span> and </span><a href="https://github.com/DataTalksClub/llm-zoomcamp"><span>LLM Zoomcamp</span></a><span> participants:</span></p><ul><li><p><a href="https://github.com/thetsuwin66/ai-diet-coach-agent"><span>AI Diet Coach</span></a><span>: Clear problem statement, live demo, screenshots, architecture diagram, and monitoring dashboards.</span></p></li><li><p><a href="https://github.com/55382/Research_Radar_agent_phd_assistant"><span>Research Radar</span></a><span>: Strong one-line description, clear evaluation results, parameter tuning, deployment, CI, and 67 tests.</span></p></li><li><p><a href="https://github.com/bielacki/cinerag"><span>CineRAG</span></a><span>: Separate retrieval and answer-quality evaluations, with datasets, metrics, results, and instructions.</span></p></li><li><p><a href="https://github.com/marcoteran/applied-ml-teaching-copilot"><span>Applied ML Teaching Copilot</span></a><span>: Detailed evaluation process, 60 test scenarios, clear test separation, and self-evaluation against the rubric.</span></p></li></ul><p><span>Other good examples:</span></p><ul><li><p><a href="https://github.com/leo-cabibihan/chess-coach-agent"><span>Chess Coach Agent</span></a></p></li><li><p><a href="https://github.com/elgrassa/CapstoneMealMapSimplified"><span>Meal Map</span></a></p></li><li><p><a href="https://github.com/larsvasseldonk/datawarehouse_agent"><span>Datawarehouse Agent</span></a></p></li><li><p><a href="https://github.com/marcelonieva7/AA_Bot"><span>AA Bot</span></a></p></li></ul><p><span>More projects are available on the </span><a href="https://courses.datatalks.club/ai-buildcamp-3/projects"><span>AI Engineering Buildcamp Cohort 3</span></a><span> and </span><a href="https://courses.datatalks.club/llm-zoomcamp-2025/projects"><span>LLM Zoomcamp 2025</span></a><span> project pages.</span></p><h2><span>What I&#8217;ve Been Working on This Week</span></h2><h3><span>AI Dev Tools Zoomcamp 2026 starts on August 31</span></h3><p><span>Registration is open for the next cohort of AI Dev Tools Zoomcamp.</span></p><p><span>This is a free hands-on course about using AI developer tools in real software development workflows.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JXuY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JXuY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 424w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 848w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 1272w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JXuY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png" width="1456" height="1004" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1004,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:379177,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/207284212?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JXuY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 424w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 848w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 1272w, https://substackcdn.com/image/fetch/$s_!JXuY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd13fbaa-938e-4de9-958f-c5cd3937691f_1792x1236.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>I&#8217;m preparing the 4 new workshops to update all of the course content:</span></p><ul><li><p><span>&#8203;</span><a href="https://luma.com/lmkti8zj"><span>AI-Native Developer Workflow: Using AI Tools Without Losing Control</span></a></p></li><li><p><span>&#8203;</span><a href="https://luma.com/50kvfku2"><span>Build and Ship an AI-Assisted Full-Stack App</span></a></p></li><li><p><span>&#8203;</span><a href="https://luma.com/ap4l3qlj"><span>Coding Agent Capabilities: MCP, Skills, Plugins, and Custom Agents</span></a></p></li><li><p><span>&#8203;</span><a href="https://luma.com/ycsfxigi"><span>Open-Source AI Tools for Security, Audit, and DevOps</span></a></p></li></ul><div class="callout-block" data-callout="true"><p><span>Join me there and </span><a href="https://courses.datatalks.club/register/ai-dev-tools/"><span>register for the 2026 cohort</span></a><span>.</span></p></div><h2>Resource: Turn AI agent traces into useful data</h2><div id="youtube2-A0LmmZf-ggM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;A0LmmZf-ggM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/A0LmmZf-ggM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Coding agents such as Claude Code, Codex, and Copilot produce detailed traces about sessions, model usage, tool calls, token consumption, and cost. In this hands-on LLM Zoomcamp workshop, Alona  shows how to turn those raw logs into structured data and dashboards.</p><p>You&#8217;ll learn how to:</p><ul><li><p>Ingest local coding-agent traces and logs from an API</p></li><li><p>Build data pipelines with dlt and load the results into DuckDB</p></li><li><p>Explore usage, models, projects, and token consumption in Marimo dashboards</p></li><li><p>Deploy pipelines and reports so they can run regularly and be shared with a team</p></li></ul><p>The workshop is part of LLM Zoomcamp, but you can follow it independently.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.youtube.com/live/A0LmmZf-ggM?si=s9JboL7Wfi3sKxA1&quot;,&quot;text&quot;:&quot;Watch the workshop&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.youtube.com/live/A0LmmZf-ggM?si=s9JboL7Wfi3sKxA1"><span>Watch the workshop</span></a></p><div><hr></div><p><span>Edited by </span><a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[My PyPI Release Pipeline for Python Libraries]]></title><description><![CDATA[How I release Python packages with agents and without]]></description><link>https://aishippingblog.com/p/my-pypi-release-pipeline-for-python</link><guid isPermaLink="false">https://aishippingblog.com/p/my-pypi-release-pipeline-for-python</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Sat, 11 Jul 2026 11:11:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_2C3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>I published </span><a href="https://pypi.org/project/keras-image-helper/#history"><span>my first Python library in early 2021</span></a><span>. Since then, I&#8217;ve </span><a href="https://pypi.org/user/alexeygrigorev/"><span>released 24 packages on PyPI</span></a><span>, and I use them regularly in my own projects.</span></p><p><span>I used to do everything manually, but in 2025, I started using coding agents and creating more libraries.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>Eventually, I automated the whole process. In this article, I&#8217;ll tell you how I do it and walk you through the entire workflow:</span></p><ul><li><p><span>Starting a library and choosing a name</span></p></li><li><p><span>Publishing the first version</span></p></li><li><p><span>Releasing through CI</span></p></li><li><p><span>Automating the process with agents</span></p></li></ul><h2><span>1. Start with an Idea</span></h2><p><span>I build something to solve a specific problem that I&#8217;m facing. Then I realize I may want to reuse it, either as a command-line utility or as a small library for other projects.</span></p><p><span>Before I create a repo, I do a brief validation step. I try to understand what I&#8217;m building and whether it is worth packaging as a library.</span></p><p><span>I usually start brainstorming and researching with ChatGPT. I explain the problem, explore what the library should do, and check whether similar tools already exist. If they do, I look at how they solve the problem and whether they fit my needs.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_2C3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_2C3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 424w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 848w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 1272w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_2C3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png" width="1456" height="597" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/118ae795-5277-4993-9caa-a094db625aae_2048x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:597,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_2C3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 424w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 848w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 1272w, https://substackcdn.com/image/fetch/$s_!_2C3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F118ae795-5277-4993-9caa-a094db625aae_2048x840.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Me looking for a library that didn&#8217;t exist, so I created SQLiteSearch</figcaption></figure></div><p><span>This also gives me a clear brief I can use if I decide to move forward with the library.</span></p><p><span>I described this process before when I wrote about building </span><a href="https://alexeyondata.substack.com/p/how-i-built-sqlitesearch-a-lightweight"><span>SQLiteSearch</span></a><span>.</span></p><h2><span>2. Choose a Name</span></h2><p><span>Once I have a clear brief, I choose a name. This is harder than it sounds. I need a name that fits the project, is short enough to use, and is still available on PyPI.</span></p><p><span>I usually start a new session with an agent and paste the exported conversation describing the problem I created in the previous step. In this prompt, I also explain that I want to turn the description into a library, and ask the agent to help me brainstorm names. I also ask it to check whether each name is available.</span></p><p><span>The availability check is pretty simple, because PyPI exposes package metadata at this URL:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;ecb5ae74-ca66-4aac-8434-d3f66f1aea0c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">https://pypi.org/pypi/{name}/json</code></pre></div><p><span>If the request returns </span><code>404</code><span>, the name is available. If it returns </span><code>200</code><span>, someone has already taken it.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jU34!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jU34!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 424w, https://substackcdn.com/image/fetch/$s_!jU34!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 848w, https://substackcdn.com/image/fetch/$s_!jU34!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 1272w, https://substackcdn.com/image/fetch/$s_!jU34!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jU34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png" width="1456" height="783" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:783,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jU34!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 424w, https://substackcdn.com/image/fetch/$s_!jU34!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 848w, https://substackcdn.com/image/fetch/$s_!jU34!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 1272w, https://substackcdn.com/image/fetch/$s_!jU34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65828b6b-c122-4407-ad91-2588594b0ab4_2048x1102.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Selecting a project name (quotex is actually available!)</figcaption></figure></div><p><span>Then I iterate. If a name is taken, I ask for more options. If a name is available but does not feel right, I keep brainstorming.</span></p><p><a href="https://alexeyondata.substack.com/i/203724093/heru"><span>Heru</span></a><span> is one example. The idea was &#8220;one tool to rule them all&#8221; (all the agents), so I wanted a short name connected to The Lord of the Rings, maybe something Elvish. I brainstormed with the agent, checked availability along the way, and eventually landed on Heru.</span></p><p><a href="https://alexeyondata.substack.com/i/203724093/quse"><span>Quse</span></a><span> was similar. I wanted a short name that still meant something. After several iterations, I landed on Quse, short for &#8220;quota use.&#8221;</span></p><p><span>I usually spend around 10 minutes on this step. By the end, I have a name that fits the project, is available on PyPI, and works for both the GitHub repo and the package.</span></p><p><span>I wrote more about how Heru and Quse got their names in </span><a href="https://alexeyondata.substack.com/p/six-projects-that-didnt-make-it"><span>Six Projects That Didn&#8217;t Make It</span></a><span>.</span></p><p></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ef0e539d-9042-4148-a473-f98cc36c43a0&quot;,&quot;caption&quot;:&quot;I have a lot of projects that I started with some grand idea, spent time on, and then never really did anything with.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Six Projects That Didn&#8217;t Make It&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2259961,&quot;name&quot;:&quot;Alexey Grigorev&quot;,&quot;bio&quot;:&quot;15 years of experience. Teaching AI and Data to 100k+ students. Founder of DataTalks.Club &#183; Creator of the Zoomcamp Series &#183; ML Engineer &amp; Author&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54a0ec21-251b-49e2-aba6-93c13f3cd8cb_800x800.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-06-26T21:07:46.828Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!BQAt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb6e334a-b051-4876-8133-83390b9d3120_1768x1086.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/six-projects-that-didnt-make-it&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:203724093,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:0,&quot;publication_id&quot;:7154940,&quot;publication_name&quot;:&quot;Alexey On Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!otON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe2d46bd-51fb-428c-923c-aa76536f9595_800x800.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2><span>3. Create the First Version</span></h2><p><span>Once I choose the name, I create a GitHub repository and publish its first version.</span></p><p><span>I usually make the repo public for two reasons:</span></p><ul><li><p><span>I contribute a lot to open source, and I want others to see the code, use it, and maybe contribute to it too</span></p></li><li><p><span>Public repos get a larger GitHub Actions quota than private repos</span></p></li></ul><p><span>At this stage, I don&#8217;t need a complete library. I create a skeleton: a minimal working version that I can publish.</span></p><p><span>I also decide the project structure up front. Across my projects, I keep this structure fairly consistent. I use Hatch, with </span><code>hatchling</code><span> as the build backend, and </span><code>uv</code><span> for tooling.</span></p><p><span>For a minimal library, I use a layout like this:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;deddb3d7-2805-4db8-8d79-e39e2e83074f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">library_name/
&#9500;&#9472;&#9472; library_name/__init__.py
&#9500;&#9472;&#9472; library_name/__version__.py        # __version__ = &#8220;0.0.1&#8221;
&#9500;&#9472;&#9472; library_name/cli.py                # only if the package installs a CLI
&#9500;&#9472;&#9472; tests/__init__.py
&#9500;&#9472;&#9472; pyproject.toml
&#9500;&#9472;&#9472; Makefile
&#9500;&#9472;&#9472; README.md
&#9500;&#9472;&#9472; .gitignore
&#9500;&#9472;&#9472; .python-version
&#9492;&#9472;&#9472; uv.lock</code></pre></div><p><span>I put the package metadata in </span><a href="https://github.com/alexeygrigorev/minsearch/blob/main/pyproject.toml"><span>pyproject.toml</span></a><span>:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;toml&quot;,&quot;nodeId&quot;:&quot;bf2177be-45df-4eec-960b-7a967b86761d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-toml">[build-system]
requires = [&#8221;hatchling&#8221;]
build-backend = &#8220;hatchling.build&#8221;

[project]
name = &#8220;&lt;library_name&gt;&#8221;
dynamic = [&#8221;version&#8221;]
description = &#8220;&lt;package-description&gt;&#8221;
readme = &#8220;README.md&#8221;
license = {text = &#8220;WTFPL&#8221;}
authors = [{name = &#8220;Alexey Grigorev&#8221;, email = &#8220;alexey@datatalks.club&#8221;}]
requires-python = &#8220;&gt;=3.12&#8221;
dependencies = [&#8221;&lt;dependencies&gt;&#8221;]

[tool.hatch.build.targets.wheel]
packages = [&#8221;&lt;library_name&gt;&#8221;]

[tool.hatch.version]
path = &#8220;&lt;library_name&gt;/__version__.py&#8221;</code></pre></div><p><span>If the library has a command-line tool, I add an entry point so users can run the command after installation:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;f9d90693-e716-4ba0-925f-e8971fabace4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">[project.scripts]
stylint = &#8220;&lt;library_name&gt;.cli:main&#8221;</code></pre></div><p><span>That is the whole skeleton: a package directory, a version file, a place for tests, and a </span><code>pyproject.toml</code><span> file with the build configuration.</span></p><h2><span>4. Publish the Project</span></h2><p><span>Once the first version is ready, I can publish it.</span></p><p><span>For manual publishing, I need two accounts: one on Test PyPI and one on PyPI. I use Test PyPI to verify that the package builds correctly and installs as expected before publishing it to the real index.</span></p><p><span>I also need API tokens for both accounts. I generate a token in each account and put both tokens in </span><code>~/.pypirc</code><span> in my home folder. I keep separate token-authenticated entries for </span><code>pypi</code><span> and </span><code>testpypi</code><span>:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;632f7e4c-f466-4be6-b3fb-f29d6b0f6add&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">[pypi]
username = __token__
password = pypi-...

[testpypi]
username = __token__
password = pypi-...</code></pre></div><p><span>Hatch uses these credentials.</span></p><p><span>With the tokens configured, the manual publishing flow is simple:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;818dcd0b-d89d-48cd-9560-bb36eba43a36&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">uv run hatch build
uv run hatch publish --repo test
uv run hatch publish</code></pre></div><p><span>I build the package, publish it to Test PyPI, check that everything looks right, and then publish it to PyPI.</span></p><p><span>This is the manual flow. I </span><a href="https://github.com/alexeygrigorev/minsearch#development"><span>documented it in the minsearch README</span></a><span> so I would not have to remember the commands every time.</span></p><h2><span>5. Set Up CI/CD</span></h2><p><span>I can publish manually, but I prefer to release through CI. I want releases to work the same way on any computer I use. It should not matter whether I have the right tokens on my machine or whether my local environment is configured correctly.</span></p><p><span>Instead of running </span><code>hatch publish</code><span> on my own machine, I let GitHub Actions publish the package when I push a version tag.</span></p><p><span>Before I release, I bump the version. For a simple bug fix, I bump the patch version. For a more substantial change, or for a breaking change, I bump the minor version. I rarely update the major version.</span></p><p><span>I put the publishing workflow in </span><a href="https://github.com/alexeygrigorev/minsearch/blob/main/.github/workflows/publish.yml"><span>.github/workflows/publish.yml</span></a><span>. GitHub Actions run it whenever I push a tag that starts with v. The workflow does three things:</span></p><ul><li><p><span>builds the package with </span><code>uv</code><span> build</span></p></li><li><p><span>checks that the built package version matches the tag</span></p></li><li><p><span>publishes the package with the official </span><code>pypa/gh-action-pypi-publish</code><span> action</span></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!31LN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!31LN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 424w, https://substackcdn.com/image/fetch/$s_!31LN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 848w, https://substackcdn.com/image/fetch/$s_!31LN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 1272w, https://substackcdn.com/image/fetch/$s_!31LN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!31LN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png" width="1456" height="732" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:732,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!31LN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 424w, https://substackcdn.com/image/fetch/$s_!31LN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 848w, https://substackcdn.com/image/fetch/$s_!31LN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 1272w, https://substackcdn.com/image/fetch/$s_!31LN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F512e1d5e-39d8-470d-9517-b2c00f271eb1_2048x1030.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>For authentication, I store the PyPI token as a GitHub secret called </span><code>PYPI_API_TOKEN</code><span>. This is the same token I keep in </span><code>~/.pypirc</code><span>, but GitHub stores it as a repo secret. At release time, I do not need the token on my machine.</span></p><p><span>So the release process is:</span></p><ol><li><p><span>Bump the version in </span><code>__version__.py</code></p></li><li><p><span>Commit the change</span></p></li><li><p><span>Create a </span><code>v&lt;version&gt;</code><span> tag</span></p></li><li><p><span>Push the tag</span></p></li></ol><p><span>In code, it looks like that:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;3daae074-d8e1-44b1-9cb6-1784144b7df8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">git tag &#8220;$VERSION&#8221;
git push origin &#8220;v$VERSION&#8221;</code></pre></div><p><span>When I push the tag, GitHub Actions builds the package and uploads it to PyPI.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2V33!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2V33!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 424w, https://substackcdn.com/image/fetch/$s_!2V33!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 848w, https://substackcdn.com/image/fetch/$s_!2V33!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 1272w, https://substackcdn.com/image/fetch/$s_!2V33!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2V33!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png" width="1456" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:608,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2V33!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 424w, https://substackcdn.com/image/fetch/$s_!2V33!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 848w, https://substackcdn.com/image/fetch/$s_!2V33!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 1272w, https://substackcdn.com/image/fetch/$s_!2V33!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe483ab59-e793-4828-9445-0bf12da85ddb_2048x855.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>This approach is not specific to Python. I can use the same flow for other ecosystems: push a tag through GitHub, then let CI run whatever publishing logic the project needs. That can mean publishing to PyPI, another registry, building binaries, or uploading artifacts elsewhere. The only requirement is that CI has access to the necessary credentials.</span></p><p><a href="https://github.com/alexeygrigorev/rustkyll"><span>rustkyll</span></a><span> is a good example. It is a static site generator written in Rust. When I push a v* tag, its workflow cross-compiles binaries for six platforms, attaches the binaries to a GitHub Release, and publishes binary-bundled wheels to Test PyPI and then to PyPI. The publishing logic is different, but the trigger is the same: push a version tag.</span></p><h2><span>6. Automate Publishing with Skills</span></h2><p><span>Once I have the manual and CI flows, I can stop repeating the setup by hand. I use a few agent skills that cover the whole pipeline:</span></p><ul><li><p><a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/init-library"><span>init-library</span></a></p></li><li><p><a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/setup-pypi-ci"><span>setup-pypi-ci</span></a></p></li><li><p><a href="https://github.com/alexeygrigorev/.agents/tree/main/skills/release"><span>release</span></a></p></li></ul><p><span>I use </span><code>init-library</code><span> to scaffold a new Python package. I give it a name, a short description, the dependencies, and whether the package needs a CLI.</span></p><p><span>The skill creates the standard layout:</span></p><ul><li><p><span>the package directory,</span></p></li><li><p><code>__init__.py</code><span>,</span></p></li><li><p><code>__version__.py</code><span> seeded at </span><code>0.0.1</code><span>,</span></p></li><li><p><span>a </span><code>tests</code><span> directory,</span></p></li><li><p><code>Pyproject.toml</code><span>,</span></p></li><li><p><span>Makefile, and</span></p></li><li><p><code>README.md</code><span>.</span></p></li></ul><p><span>It also sets up the project with </span><code>uv</code><span>. Once I have the repo on GitHub, I move to the next skill.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WN3d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WN3d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 424w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 848w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 1272w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WN3d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png" width="1456" height="987" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:987,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WN3d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 424w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 848w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 1272w, https://substackcdn.com/image/fetch/$s_!WN3d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578d4c0f-a540-45ed-aacf-a0fbf0cb5684_2048x1389.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>I use </span><code>setup-pypi-ci</code><span> to set up tag-triggered CI publishing. The skill copies the publish.yml workflow, adds the </span><code>make release</code><span> target, reads the PyPI token from </span><code>~/.pypirc</code><span>, and saves it in GitHub as the </span><code>PYPI_API_TOKEN</code><span> secret. After that, I can release the package by bumping the version and pushing a tag.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OSnX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OSnX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 424w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 848w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 1272w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OSnX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png" width="1456" height="851" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:851,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OSnX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 424w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 848w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 1272w, https://substackcdn.com/image/fetch/$s_!OSnX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac679f25-6627-4fcd-abe3-c2eb5066508e_2048x1197.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>I use </span><code>release</code><span> to make a release. By default, it bumps the patch version, commits the change, pushes the tag, watches the GitHub Actions run until it finishes, and writes GitHub release notes from the git log. The skill is registry-agnostic: I can use it for PyPI, crates.io, npm, or anything else that publishes from CI when I push a </span><code>v*</code><span> tag.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UkRV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UkRV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 424w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 848w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 1272w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UkRV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png" width="1456" height="877" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:877,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UkRV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 424w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 848w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 1272w, https://substackcdn.com/image/fetch/$s_!UkRV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db66528-c586-4125-9e8a-372a9187707c_2048x1233.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>I keep these skills in my </span><a href="https://github.com/alexeygrigorev/.agents"><span>.agents repo</span></a><span>, which works as my AI assistant dotfiles. It gives me one source of truth for the same skills across Claude Code, Codex, and OpenCode. So I can use the same release process with every agent I use.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZvQ-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 424w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 848w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png" width="1456" height="941" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 424w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 848w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvQ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe303a0b4-d197-4de1-a963-2fdd5250094e_1794x1160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>This workflow helps me maintain more and more libraries and create new ones quickly. What used to be a manual process is now a set of reusable skills.</span></p><h2><span>What I&#8217;ve Been Working on Recently</span></h2><h3>1) Ingesting Agent Traces with dltHub</h3><div id="youtube2-A0LmmZf-ggM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;A0LmmZf-ggM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/A0LmmZf-ggM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>This week, we ran a hands-on workshop with dltHub on ingesting agent traces.</p><p>When you build AI agents, you need to understand what they are doing. You need to see tool calls, intermediate steps, token usage, outcomes, and failures rather than leaving them hidden in logs.</p><p>In the workshop, we used dltHub Pro to build a pipeline that captures agent traces, normalizes them into a consistent schema, and transforms nested, variable-length trace data into tables we can query and analyze.</p><p>Then we used this data to build reports. These reports help us understand how agents behave, where they fail, and what we can improve. The goal was to stop flying blind and create a reporting layer that makes agent debugging more practical.</p><h3>2) Updating the Agent Guardrails Workshop</h3><p>I also updated the workshop on building an agent with guardrails.</p><p>In this workshop, we build a Data Engineering Zoomcamp FAQ agent. We start with a simple RAG baseline, turn it into an agent with one search tool, and then add guardrails around it.</p><p>We add input checks for topic and intent, output checks for unsafe promises or policy violations, and multiple guardrails with clear responsibilities. We also run guardrail checks concurrently so they do not add too much latency.</p><p>We write the examples in Python with Pydantic, minsearch, and the plain OpenAI Python SDK. I wanted the approach to stay framework-neutral, so we implement the agent loop and the guardrails directly instead of relying on a specific agent framework.</p><p>We take inspiration from the OpenAI Agents SDK guardrails, but we implement the guardrails ourselves. If your agent framework does not provide input and output guardrails, you can still use the same approach.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/workshops/agent-with-guardrails&quot;,&quot;text&quot;:&quot;See the workshop&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/workshops/agent-with-guardrails"><span>See the workshop</span></a></p><h3>3) Adapting a Resume with AI</h3><p>Another workshop was about adapting a resume with AI.</p><p>We built a pipeline that starts with a general resume for an engineering role. Once we have that base version, we adapt it to a specific domain, for example, education. At this stage, we make the experience most relevant to that domain more visible.</p><p>Then we take one more step: we adapt the domain-specific resume to a concrete company. Instead of rewriting the resume randomly for every application, we move through three levels:</p><ul><li><p>General resume for the role</p></li><li><p>Resume adapted to the domain</p></li><li><p>Resume adapted to a specific company in that domain</p></li></ul><p>That was the main idea of the workshop: use AI not just to rewrite a resume, but to create a structured process for positioning your experience.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/workshops/tailor-cv-ai-engineering&quot;,&quot;text&quot;:&quot;See the workshop&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/workshops/tailor-cv-ai-engineering"><span>See the workshop</span></a></p><p></p><h2>Interesting Tools</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qfP8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qfP8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 424w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 848w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 1272w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qfP8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png" width="1456" height="909" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:909,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:291095,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/199753912?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!qfP8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 424w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 848w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 1272w, https://substackcdn.com/image/fetch/$s_!qfP8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fbc10a7-c08f-4841-b8eb-79ad15191461_1752x1094.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><a href="https://github.com/microsoft/webwright">Webwright</a><span> is a lightweight, terminal-based browser agent framework from Microsoft that gives an LLM a CLI to spawn browser sessions and complete web tasks. Instead of the step-by-step Playwright MCP loop, it has the model write a re-runnable Python Playwright script end-to-end, so the agent&#8217;s browsing history becomes a single code file you can rerun, adapt, and debug. It&#8217;s a CLI alternative to Playwright MCP for browser testing and automation, built on just httpx, pydantic, playwright, and typer, with OpenAI, Anthropic, and OpenRouter backends.</span></p></li></ul><div><hr></div><p><span>Edited by </span><a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Choosing a Portfolio Project. The Definite Guide]]></title><description><![CDATA[When preparing personal plans for AI Shipping Labs members, I often get the question, &#8220;What should I build for my portfolio?&#8221; There are too many options, and no clear way to pick one.]]></description><link>https://aishippingblog.com/p/choosing-a-portfolio-project-the</link><guid isPermaLink="false">https://aishippingblog.com/p/choosing-a-portfolio-project-the</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 03 Jul 2026 15:17:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qTlw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>When preparing personal plans for AI Shipping Labs members, I often get the question, &#8220;What should I build for my portfolio?&#8221; There are too many options, and no clear way to pick one.</span></p><p><span>In this newsletter, I want to share a repeatable process for selecting your next portfolio project. We focus on the AI Engineering roles, but it will also work for any role in engineering or data.</span></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><span>Why Building a Portfolio Project in the First Place</span></h2><p><span>First of all, an active portfolio that you usually store on your GitHub profile raises your chances of getting hired, because most hiring managers look at it. Additionally, it helps others notice your work, leading to collaboration, freelance offers, and conversations you wouldn&#8217;t have otherwise. You can think of it as your landing page that presents you and your skills without requiring you to talk about yourself.</span></p><h2><span>Types of Portfolio Projects</span></h2><p><span>I divide all the projects into several categories, and I think this categorization may also help you better understand what could become your portfolio project.</span></p><h3><span>1) Role-targeted projects</span></h3><p><span>Role-targeted projects are the projects that you build to get hired for a specific role. You pick a domain, research the companies in it, and build something that speaks to their work.</span></p><p><span>In this post, we&#8217;ll focus on role-targeted projects, and the framework I describe here gives you a way to find ideas for them. But it&#8217;s also helpful to know about other project types I collected below.</span></p><h3><span>2) Personal projects</span></h3><p><span>These are the kinds of projects that solve the problems that you yourself regularly face and want to build a solution for them.</span></p><p><span>Most personal projects might not be directly related to the field you&#8217;re applying to. And, individually, they aren&#8217;t strong portfolio pieces. But together they show that you notice problems and build tools to solve them.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!83uN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!83uN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 424w, https://substackcdn.com/image/fetch/$s_!83uN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 848w, https://substackcdn.com/image/fetch/$s_!83uN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 1272w, https://substackcdn.com/image/fetch/$s_!83uN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!83uN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg" width="1456" height="976" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:976,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:92950,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/svg+xml&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!83uN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 424w, https://substackcdn.com/image/fetch/$s_!83uN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 848w, https://substackcdn.com/image/fetch/$s_!83uN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 1272w, https://substackcdn.com/image/fetch/$s_!83uN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9b26e89-4ec0-4dd8-8a92-a70736e84d5f_1202x806.svg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>I have many such projects. When something in my work or life is annoying, and I see a fix, I build it. For example, I have a tool called </span><a href="https://alexeyondata.substack.com/i/192289520/2-ssh-auto-forward-port-forwarding-without-vs-code"><span>ssh-autoforward</span></a><span> that helps me manage my remote servers. It scans the remote ports and forwards a port the moment it opens</span></p><h3><span>3) Take-home assignments</span></h3><p><span>When a company gives you a take-home assignment as part of an interview, the code you produce is yours. That&#8217;s why take-home assignments can become portfolio projects, too.</span></p><p><span>This is especially fair when the company doesn&#8217;t pay you for the interview. Publish it, say it was part of an interview process, and show how you code.</span></p><h3><span>4) Hackathon, community, and open-source projects</span></h3><p><span>Any project that you created in public or contributed to can also become your portfolio project. It can be your hackathon contribution, community work, or contributions to open-source projects.</span></p><p><span>The strongest signal for hiring managers is that other people saw, reviewed, or used your work&#8217;s product, meaning it serves others, not just you.</span></p><p><span>Now that we&#8217;ve covered why you should have a portfolio and what to fill it with, let&#8217;s move to the framework itself and cover each step separately.</span></p><h2><span>The Project-Selection Framework</span></h2><p><span>There are two broad job search strategies that you can use.</span></p><h3><span>Spray-and-Pray Strategy</span></h3><p><span>The first one is more chaotic, less controllable, and less efficient. It&#8217;s called spray-and-pray. You apply to every job you can find and hope someone responds. You don&#8217;t have any selection criteria or filters to select job offerings you want to apply for. You spread your effort, and that strategy doesn&#8217;t guarantee you meaningful results.</span></p><h3><span>Portfolio Strategy with Domain-Based Selection</span></h3><p><span>The second option is more focused. You think about which domains and companies you want to work for and use it as a North Star that guides you through the skills you need to pick up and projects to build. It doesn&#8217;t mean you stop applying to other jobs. You still can, but you have a direction that you&#8217;re putting your efforts into. And it also means your project starts to have a narrower scope, and you can tune it later and add details.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zRhS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zRhS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 424w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 848w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 1272w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zRhS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png" width="1456" height="404" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/460008ab-50bd-4954-a904-117bf4842c85_1672x464.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:404,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:628610,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!zRhS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 424w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 848w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 1272w, https://substackcdn.com/image/fetch/$s_!zRhS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F460008ab-50bd-4954-a904-117bf4842c85_1672x464.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>And here are the 6 steps that describe the project selection process:</span></p><ol><li><p><span>Pick a domain where you want to work.</span></p></li><li><p><span>Find the companies in that domain that are (were) hiring for the role you&#8217;re applying to.</span></p></li><li><p><span>Analyze their job descriptions and engineering blog posts.</span></p></li><li><p><span>Extract the problems they solve.</span></p></li><li><p><span>Find problems that are common across several companies in the domain.</span></p></li><li><p><span>Pick one problem and design a project around it, choosing a technology stack that matches what those companies use.</span></p></li></ol><h2><span>How to Run this Framework</span></h2><p><span>You can run this framework on your own, but I&#8217;d recommend using an AI assistant to speed up your research.</span></p><p><span>Here&#8217;s what you need if you want to use AI:</span></p><ol><li><p><span>An AI assistant, like ChatGPT, Gemini, Grok, or a coding agent, like Codex or Claude Code. A coding agent can create and move files, so you keep the research organized as you go. With a chat assistant, you&#8217;ll need to manage the files yourself.</span></p></li><li><p><span>Time to read about the companies that you&#8217;ll collect. The agent can do the first pass quickly, but you still need to go through the sites, job descriptions, and blogs by hand so you understand the problems, not just the list.</span></p></li></ol><p><span>Now let&#8217;s go through the framework step by step.</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/subscribe?"><span>Subscribe now</span></a></p><h2><span>Step 1: Start from the domain</span></h2><p><span>Start by choosing a domain where you might want to work.</span></p><p><span>This is what makes the project-selection process different from the spray-and-pray strategy. Instead of applying to every role you can find, you choose a domain first and use it to guide your research, skill selection, and project ideas.</span></p><p><span>It is also better than targeting only one company. One opening can close before you finish the project. A single company may also have a very specific problem that does not transfer well to other interviews. But when several companies in the same domain address the same problem, a single portfolio project can become relevant to many conversations.</span></p><p><span>The domain does not have to be one you already know well. Existing domain knowledge helps, especially if you are switching careers, but interest is enough to start.</span></p><h2><span>Step 2: Find companies in the domain</span></h2><p><span>Once you have a domain, list 5 to 10 companies that hire for the role you want to be hired for or work on problems where this role is relevant.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qTlw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qTlw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 424w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 848w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qTlw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png" width="1456" height="1029" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1029,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1319847,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qTlw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 424w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 848w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!qTlw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cece23b-8531-40f2-83b8-a3fdca57ad00_2314x1636.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Current and past openings are useful, but they are not the only source. A company can still help with your research if it has engineering blogs, case studies, product pages, or publicly available technical material. At this stage, you are trying to understand what companies in the domain build and what problems appear across the market.</span></p><p><span>This is where you can use your AI assistant to combine your job-market data with live online search. That makes the method easier for someone else to repeat.</span></p><p><span>The request looks like this:</span></p><div class="callout-block" data-callout="true"><p><span>I want to choose a portfolio project, and I&#8217;m interested in [domain]. Find companies that work in this domain and hire [role]. Use online search to find which companies are currently hiring in [domain]. I want to target [name the role] roles related to </span>[domain]<span>.</span></p></div><h2><span>Step 3: Analyze job descriptions and tech blogs</span></h2><p><span>After you have a list of companies, the next step is to understand what they work on.</span></p><p><span>Use two sources: job descriptions and engineering blogs.</span></p><p><span>Job descriptions show what a company expects an engineer to do. They usually include responsibilities, team context, and required skills. From them, you can infer what the company is building and what kind of work it expects you to handle.</span></p><p><span>Engineering blogs show what teams have already built and what problems they had to solve. In education, for example, a blog post about an AI conversation partner can reveal problems around dialogue quality, learner feedback, grounding, evaluation, or latency. That kind of detail is useful because it gives you material for portfolio project ideas.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7nra!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7nra!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 424w, https://substackcdn.com/image/fetch/$s_!7nra!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 848w, https://substackcdn.com/image/fetch/$s_!7nra!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 1272w, https://substackcdn.com/image/fetch/$s_!7nra!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7nra!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png" width="1456" height="824" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:824,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2020995,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7nra!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 424w, https://substackcdn.com/image/fetch/$s_!7nra!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 848w, https://substackcdn.com/image/fetch/$s_!7nra!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 1272w, https://substackcdn.com/image/fetch/$s_!7nra!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fe2adf-a961-40e4-9ad1-2cb649efcb51_2880x1630.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Example of an engineering blog from Uber</figcaption></figure></div><p><span>Ask the agent to find relevant job descriptions and engineering blog posts for each company on the list, and to split them into two files:</span></p><div class="callout-block" data-callout="true"><p><span>For all these companies, find potentially relevant job descriptions, engineering blog posts, or technical case studies. Put the job descriptions in one file and the engineering blogs or case studies in another.</span></p></div><p><span>When you go through the job descriptions, do not fixate on the list of technologies. Use the tech stack as a clue, not as the project idea.</span></p><p><span>Focus on what the company does and what problem it solves with those technologies.</span></p><p><span>Once you have the job descriptions and blog posts, extract the problems from them.</span></p><h2><span>Step 4: Extract problems</span></h2><p><span>Next, turn the source material into a list of problems. But don&#8217;t jump to solutions yet.</span></p><p><span>At this stage, you only want to understand that there are people facing this problem, what input they have, and what they want to get as a result of interacting with your solution.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6vUk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6vUk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 424w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 848w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 1272w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6vUk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png" width="1456" height="1025" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1025,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1319384,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6vUk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 424w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 848w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 1272w, https://substackcdn.com/image/fetch/$s_!6vUk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2c2155-14b8-4b13-b19a-7a1fee41a32c_2320x1634.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Point the agent at the job descriptions and engineering blogs it produced, and ask it to extract the problems. Don&#8217;t forget to ask it to specify sources for each problem it finds:</span></p><div class="callout-block" data-callout="true"><p><span>Based on these job descriptions and engineering blogs, find the problems these companies solve. Focus only on problems, not solutions. Do not focus on how they use the technologies. Focus on what they use the technologies for.</span></p><p><span>Include sources. For each problem, say whether it comes from a job description, engineering blog, case study, or another source. Explain exactly where it appears and provide more context.</span></p></div><h2><span>Step 5: Find problems shared across companies</span></h2><p><span>A single-company problem is narrow. A problem that several companies care about is usually a better portfolio target than one that appears only once.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!18av!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!18av!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 424w, https://substackcdn.com/image/fetch/$s_!18av!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 848w, https://substackcdn.com/image/fetch/$s_!18av!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 1272w, https://substackcdn.com/image/fetch/$s_!18av!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!18av!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png" width="1456" height="1002" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1002,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1793040,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!18av!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 424w, https://substackcdn.com/image/fetch/$s_!18av!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 848w, https://substackcdn.com/image/fetch/$s_!18av!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 1272w, https://substackcdn.com/image/fetch/$s_!18av!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0312cb-7f33-4afb-8bbf-8e56aaf4c001_2372x1632.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Ask the agent to group the per-company problems into themes that appear across more than one company:</span></p><div class="callout-block" data-callout="true"><p><span>Find problems that are common across the domain. Show problems that more than one company is interested in solving, or common themes among these companies.</span></p></div><h2><span>Step 6: Pick a problem and choose technologies</span></h2><p><span>Now you have a list of shared problems. The next step is to pick one and turn it into a project.</span></p><h3><span>Create project candidates</span></h3><p><span>Start by generating several possible projects from one theme. This gives you options before you commit.</span></p><p><span>You can use this prompt:</span></p><div class="callout-block" data-callout="true"><p><span>Create about five possible projects from the problem list.</span></p><p><span>Use this format:</span></p><ul><li><p><span>Project: what this project is about?</span></p></li><li><p><span>User: who&#8217;s the user? </span></p></li><li><p><span>Input: which data do we need?</span></p></li><li><p><span>Output: what will it produce?</span></p></li><li><p><span>Why it fits: why should I focus on this project?</span></p></li></ul></div><h3><span>Pick one project and choose technologies that match the target companies</span></h3><p><span>Pick one project from your list and choose technologies that target companies already use, so the project reads as domain-relevant when you discuss it in an interview.</span></p><p><span>Ask the agent for the stack:</span></p><div class="callout-block" data-callout="true"><p><span>Suggest a technology stack for this project that mirrors what these companies actually use. Map each technology to the company or source it comes from.</span></p></div><p><span>The agent suggests technologies based on the data it found, but the final decision is yours.</span></p><p><span>Don&#8217;t try to learn too many new tools in one project. Pick a small set, build the project, and make it usable.</span></p><p><span>Once you have the project and stack, you are ready to build.</span></p><h2><span>Where to go from here</span></h2><p><span>You now have the full framework. From here, you can start building.</span></p><p><span>But here are a few more things that deserve extra attention, and I think it&#8217;s important to note them to you.</span></p><h3><span>Add evaluations and monitoring</span></h3><p><span>For an AI engineering portfolio or most data professionals, evaluations matter more than any other feature.</span></p><p><span>A project that generates output but never checks whether the output is correct is still incomplete.</span></p><p><span>Use an AI assistant to help you build faster, but be deliberate about these parts:</span></p><ol><li><p><span>An evaluation harness that scores the output against the criteria that matter for the problem</span></p></li><li><p><span>Logging or monitoring, so you can inspect what the system did</span></p></li><li><p><span>Tests, so the core pieces stay correct as you change the project</span></p></li><li><p><span>A README that a hiring manager can skim quickly</span></p></li></ol><h3><span>Make the README scannable</span></h3><p><span>Most hiring managers won&#8217;t read your code. They will skim the project page first and decide whether to look for more information. Optimize for that.</span></p><p><span>The README should explain, in a minute or less:</span></p><ol><li><p><span>What you built</span></p></li><li><p><span>Who it is for</span></p></li><li><p><span>What input it takes</span></p></li><li><p><span>What output it produces</span></p></li><li><p><span>How you evaluate whether it works</span></p></li><li><p><span>Why the technology choices make sense</span></p></li></ol><p><span>Ask friends or colleagues to read the README and tell you what they understood. If they cannot explain the project back to you, rewrite it.</span></p><p><span>The code still matters. If someone opens a random file, it should look like the work you did and put effort into. But the README is where most people will decide whether the project is worth a closer look.</span></p><h3><span>Make the project usable</span></h3><p><span>A project that runs and produces results is stronger than one that is technically complete but never used. Get it to a state where you can feed it real input, run it end-to-end, and get useful output back.</span></p><h3><span>Keep building in the same direction</span></h3><p><span>One project is a start. You can build more projects by using your initial shortlist of project candidates.</span></p><p><span>When working on a new project, slightly vary the focus or stack while still aiming at the same domain.</span></p><p><span>For example, one project can use OpenAI, the next one &#8211; Anthropic. Another can use a knowledge graph approach based on a case study. Over time, you build a small portfolio that shows the range of your skills and the tools you can work with, based on real market data rather than random choices.</span></p><p><span>By the time you interview, you will have projects to talk about. When the interviewer asks what you built, you can explain the domain, problem, input, output, evaluation approach, and why you chose the stack.</span></p><h3><span>Build a habit</span></h3><p><span>Landed your new job? Congratulations! However, don&#8217;t stop building. Actively look for new project ideas and consistently build a habit. This will keep your skills sharp and your approach hands-on. Over time, starting new projects will become increasingly straightforward as you refine your process and build upon your existing foundation.</span></p><p></p><p>This article is based on a workshop I recently did at AI Shipping Labs. Check our website if you want to see more details (free with signup).</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/workshops/selecting-a-portfolio-project&quot;,&quot;text&quot;:&quot;Check the Workshop&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/workshops/selecting-a-portfolio-project"><span>Check the Workshop</span></a></p><p></p><h2><span>What I&#8217;ve Been Working on Recently</span></h2><h3>1) Podwiki: A Wiki from Five Years of Podcasts</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZtPC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZtPC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 424w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 848w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZtPC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png" width="1456" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:236086,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/204912260?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZtPC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 424w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 848w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!ZtPC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ad2398-74b5-43d9-a2d1-d989f686b96c_2152x1266.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve been working on something I call <a href="https://datatalks.club/podwiki/">Podwiki</a>: a Wikipedia-style resource built from all the podcasts I&#8217;ve recorded over the last five years.</p><p>I gave the task to Codex and used the goal feature. It worked on the project for about two days and processed a lot of material. It also burned through the limits quickly, so I had to reset them several times. The result is still in progress, but it already looks useful: a large, structured knowledge base from years of podcast conversations.</p><h3>2) AI Shipping Labs: New Sprint and Small Improvements</h3><p>We also <a href="https://aishippinglabs.com/blog/ai-shipping-labs-sprint-2-july-2026">launched a new sprint in AI Shipping Labs</a>.</p><p>To prepare for it, I worked on a set of small product improvements. Nothing huge individually, but the kind of polishing work that makes the experience smoother for members once a new sprint starts.</p><h3>3) AI Dev Tools Zoomcamp New Cohort</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4_Rw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4_Rw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4_Rw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4_Rw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!4_Rw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87e4a4e1-e710-40f8-bf01-82e968a5643e_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We opened registration for the next cohort of AI Dev Tools Zoomcamp. The live cohort starts on August 31, 2026.</p><p>The course is still free, and the materials are also available in self-paced mode. But the live cohort offers the full structure: graded homework, a leaderboard, peer review, and a certificate. The self-paced version is better if you just want to go through the materials on your own, but it does not include scoring, peer review, leaderboard, or certificate.</p><p>One small but important improvement: registration is now hosted directly on the course management platform rather than in Airtable.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://courses.datatalks.club/register/ai-dev-tools/&quot;,&quot;text&quot;:&quot;Register here&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://courses.datatalks.club/register/ai-dev-tools/"><span>Register here</span></a></p><h3>4) Trying Fable for Codebase Audits</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bMbo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bMbo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bMbo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg" width="1050" height="1280" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1280,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Terminal log of a Fable codebase audit: the agents/voice/usage auditor finished with 8 findings including an all-or-nothing usage-panel parser and an unbounded cold-open transcript read that can OOM, the app-lifecycle/storage auditor finished with 7 findings headlined by a corrupt app_settings.xml crash-looping the app at launch, with three more auditors still running&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Terminal log of a Fable codebase audit: the agents/voice/usage auditor finished with 8 findings including an all-or-nothing usage-panel parser and an unbounded cold-open transcript read that can OOM, the app-lifecycle/storage auditor finished with 7 findings headlined by a corrupt app_settings.xml crash-looping the app at launch, with three more auditors still running" title="Terminal log of a Fable codebase audit: the agents/voice/usage auditor finished with 8 findings including an all-or-nothing usage-panel parser and an unbounded cold-open transcript read that can OOM, the app-lifecycle/storage auditor finished with 7 findings headlined by a corrupt app_settings.xml crash-looping the app at launch, with three more auditors still running" srcset="https://substackcdn.com/image/fetch/$s_!bMbo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bMbo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8227dd0-cf82-4357-a205-92ee247666c8_1050x1280.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve also been trying Fable while I have access to it.</p><p>One use case I&#8217;m exploring is codebase audits. I used it to run several parallel audit agents over a codebase, and the results were interesting. Two auditors finished with 15 findings between them, including issues such as an all-or-nothing usage-panel parser, an unbounded transcript read that could run out of memory, and a corrupt settings file that could cause the app to crash-loop at launch. Three more auditors were still running at that point.</p><p>This is the kind of task where parallel agents can be useful: not writing the whole system, but inspecting different parts of an existing codebase and surfacing problems that are easy to miss.</p><h3>5) New Course Concept: Product Shipping Zoomcamp</h3><p>I also spent time thinking through a new course concept: Product Shipping Zoomcamp.</p><p>The idea is an end-to-end product development course in which students take an idea from concept to a deployed, improved, measured, publicly launched, and peer-reviewed product. The key design decision is that community and build-in-public start in Module 1, not at the end. Students should not disappear for weeks, build something alone, and only show it when it is finished. They should get feedback, share progress, and improve the product throughout the course.</p><blockquote><p>If this kind of course sounds interesting, leave a comment with what you would want to see in it.</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/choosing-a-portfolio-project-the/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/p/choosing-a-portfolio-project-the/comments"><span>Leave a comment</span></a></p><h2><span>Resource: Ship Production Code 2x Faster With AI</span></h2><div id="youtube2-5HP4DQZJkNQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;5HP4DQZJkNQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/5HP4DQZJkNQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>If you want to go deeper into AI-assisted software development, we recently published a DataTalksClub workshop with Mihail Eric on shipping production code faster with AI.</p><p>The workshop goes beyond basic vibe coding and focuses on a more structured development process. Mihail walks through his RAPID framework for using AI in real codebases: onboarding to legacy systems, analyzing the architecture, constraining agents with sufficient context, preventing scope creep with PRD templates, and coordinating multiple agents without losing control over code quality.</p><div><hr></div><p><span>Edited by </span><a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item></channel></rss>