<?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>Fri, 21 Aug 2026 06:37:21 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[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_!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" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the forth article in a series based on AI Dev Tools Zoomcamp, the free course we run at DataTalks.Club.</p><p>All articles in the series:</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><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/ai-native-development-specifications">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/ai-native-development-specifications">Deploy a Full-Stack App with AI Coding Assistants</a></p></li><li><p>Part 4: DevOps and Observability for an AI-Built App (this article)</p></li><li><p>Part 5: TBA</p></li></ul><p>In part 2, we developed an end-to-end application for conducting system design interviews. In part 3, we deployed it to AWS.</p><p></p><p>In this part, we will take the application we deployed previously, and make it more production-ready:</p><ul><li><p>Define dev and prod environments</p></li><li><p>Introduce observability: logs, metrics, alerts</p></li><li><p>Use AI as the first responder when the application stops working</p></li></ul><p><strong>This article is a draft and will be updated after the workshop</strong></p><h2><strong>Overview</strong></h2><p>Right now, every push to <code>main</code> goes straight to the one environment we have, and nothing tells us the app is unhealthy until we notice by hand. We fix that in three steps: separate dev from prod so a bad change never reaches users directly, add the telemetry and alerting that would have caught last part&#8217;s failure on its own, and give an agent a narrow, auditable way to respond.</p><h2><strong>Dev and prod environments</strong></h2><p>So far, a push to <code>main</code> deploys straight to the only environment we have. That&#8217;s fine for a demo, but it means every change - tested or not - reaches the same instance our users hit.</p><p>We want a place to deploy to first, and a deliberate, separate step to promote a change from there to production.</p><p>We already have infrastructure as code for our deployment from the previous part, and it already parameterizes what differs between environments, such as the domain and the instance size. That means we don&#8217;t design anything new - we ask for a second, independent copy of what we already have, and treat what we already have as dev from now on.</p><pre><code><code>Create a second, independent copy of our deployment infrastructure for a
production environment.

It must be able to run alongside the existing one with its own database and compute. The existing becomes the dev environment.</code></code></pre><p>Once prod exists, split the deploy step in CI/CD so each environment gets its own path:</p><pre><code><code>Split our CI/CD deploy step into two:

Deploy to dev: on every push to main, after tests pass, deploy to the dev
environment automatically (we already do it today)

Deploy to prod: a manual workflow that promotes the version running in dev to prod.</code></code></pre><p>The key property is that prod never builds from source on its own - it redeploys the exact commit that already proved itself in dev. Promotion is a deliberate, logged action, not a side effect of pushing code.</p><h2><strong>Instrumenting</strong></h2><p>Two environments buy us a safe place to promote from, but not visibility. A bad deploy can still sit in prod for hours before anyone notices, because nothing yet tells us the app is unhealthy. And even once we notice, &#8220;error rate went up&#8221; isn&#8217;t enough - we need to get from that to one failed request, its log, and the commit that served it. A dashboard that only shows CPU and memory can look perfectly fine while the one thing users care about is silently failing. That&#8217;s what we fix next.</p><p>We use <a href="https://opentelemetry.io/docs/">OpenTelemetry</a> for this: a vendor-neutral format for traces, metrics, and logs. A metric tells us the canvas-broadcast error rate went up; a trace shows one failed request&#8217;s path through the backend; a log holds the exception. Tagging all three with the same service name, environment, and deployed commit is what lets us walk from a spike on a chart to the one request, and the one log line, behind it.</p><p>We ask the coding assistant to instrument one important operation first:</p><pre><code><code>Instrument the FastAPI backend with OpenTelemetry.

Export traces and metrics with OTLP (no collector yet). Include service name, environment and deployed git commit</code></code></pre><p>The app sends OTLP to an <a href="https://opentelemetry.io/docs/collector/">OpenTelemetry Collector</a>, which exports it to whichever backends we pick.</p><p>Exporting straight from the app to each backend instead would mean an application change every time a backend changes, and a slow backend could stall the app&#8217;s own exporters. The collector is one stable boundary that handles batching and retries outside the request path.</p><pre><code><code>Add OTLP collector.

Create the observability/ directory with Docker Compose for an OpenTelemetry
Collector, Prometheus, Loki, Tempo, and Grafana.</code></code></pre><h2><strong>Build the dashboard</strong></h2><p>The collector is running, but nothing draws it yet. We ask for one panel - the one the rest of this article leans on:</p><pre><code><code>Add one Grafana panel: canvas-broadcast error rate, with exemplars enabled so
a point links to its trace.</code></code></pre><h2><strong>Look at it locally first</strong></h2><p>Before shipping anywhere, we run the stack next to the app on our own machine and generate one real trace, so we know the prompt above actually worked before going looking for it in dev or prod:</p><pre><code><code>docker compose -f observability/docker-compose.yaml up -d</code></code></pre><p>Then we hit the app a few times so there&#8217;s something to see, and open Grafana at </p><p>http://localhost:3000</p><p> - no tunnel needed here, since nothing on a laptop is public in the first place. Once the panel we just asked for shows real numbers, it&#8217;s safe to ship.</p><h2><strong>Run it in dev and prod, and reach it privately</strong></h2><p>A demo dashboard on a laptop can&#8217;t tell us about a real failure, so this stack needs to run next to the app in every environment. We deploy it the same way as everything else - push to main, dev picks it up automatically, then a promotion ships it to prod:</p><pre><code><code>Deploy the observability/ stack alongside the app, in every environment.</code></code></pre><h2><strong>Follow one failed request</strong></h2><p>Let&#8217;s break the app:</p><pre><code><code>Introduce a bug that makes canvas broadcasts fail for a fraction of calls.
</code></code></pre><p>Same pipeline as before: main, then dev, then a promotion to prod. Then we use the app normally until one edit fails.</p><p>With that one failure in hand, we open the dashboard and answer these, in order, by moving between panels rather than guessing:</p><ol><li><p>Did users receive errors? - the canvas-broadcast error-rate panel moved.</p></li><li><p>Which operation failed? - that&#8217;s the panel that moved; the metric is already labeled by operation.</p></li><li><p>Can we open one representative request? - click an exemplar on that panel to jump straight to one trace.</p></li><li><p>Which log belongs to that request? - the trace carries a trace ID; look it up in Loki.</p></li><li><p>Which deployment served it? - the trace and the log both carry the deployed commit as an attribute.</p></li></ol><p>A dashboard that can&#8217;t answer these - even a good-looking one - hasn&#8217;t instrumented the incident we actually care about.</p><h2><strong>Create one actionable alert</strong></h2><p>We don&#8217;t import a large alert pack - just one alert for a sustained increase in failed canvas broadcasts, requiring both:</p><ul><li><p>enough requests failed to affect users</p></li><li><p>the failure lasted long enough that one brief blip doesn&#8217;t page anyone</p></li></ul><p>This follows the <a href="https://prometheus.io/docs/practices/alerting/">Prometheus alerting guidance</a>: keep alerts few, tolerate small blips, and page on symptoms that need action. CPU, memory and connection counts still belong on the dashboard - they help explain an incident, they just don&#8217;t need to wake anyone up on their own.</p><p>We ask the assistant for the rule and its test together:</p><pre><code><code>Create a Prometheus alert for a sustained canvas-broadcast error rate.
</code></code></pre><h2><strong>Wake the on-call engineer</strong></h2><p>Everything from here lives in one folder in the app repo, <code>on-call-engineer/</code>. Rather than stand up something to receive a webhook, we poll: a script checks Prometheus&#8217;s alert API on an interval and starts the on-call agent the moment our alert shows up firing.</p><pre><code><code>Add an on-call-engineer/ folder with a script that polls Prometheus's
/api/v1/alerts every minute.</code></code></pre><p>For this proof of concept, that script runs from a cron job on our own machine. Nothing needs to be reachable from the internet for this to work - the script reaches out to Prometheus, not the other way around.</p><p>A cron job on a laptop only works while someone remembers to leave it running, though. The production version of the same idea is a scheduled serverless function - an AWS Lambda on an EventBridge schedule, a Cloud Run job on Cloud Scheduler, or your platform&#8217;s equivalent - firing the identical poll script every minute. Nothing runs between checks: the platform starts the function, it polls, and if the alert isn&#8217;t firing it exits and gets torn down immediately. That&#8217;s a stronger version of &#8220;provisioned only when needed&#8221; than standing up a box on alert, since even the polling itself costs nothing while the app is healthy. We don&#8217;t build that here, but the poll script doesn&#8217;t change to get there - only what&#8217;s calling it on a timer.</p><h2><strong>Let it investigate and fix</strong></h2><p>The agent isn&#8217;t handed a pre-built evidence bundle - it collects its own, with read access to exactly the systems it needs: Prometheus, Loki, Tempo, and <code>git log</code>. And it&#8217;s allowed to do more than look: edit code in a fresh checkout, run the test suite, and push a fix if it has one.</p><pre><code><code>You are the on-call agent for one production alert: {alert.json}

You have read access to Prometheus, Loki, Tempo, and git log. Investigate
first - write your evidence and reasoning to incidents/&lt;timestamp&gt;/, and
don't call something the root cause unless the evidence rules out the main
alternatives.

If you find a fix you're confident in, make it on a fresh branch, run the
test suite, and push only if it passes. If you're not confident, or nothing
you tried passes, don't push - write an escalation instead (see below).</code></code></pre><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[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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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>Part 4 and 5: TBA</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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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><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[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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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 and 5: TBA</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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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>The remaining modules will build on the same app:</p><ul><li><p>Deployment: we will deploy the application to the cloud and set up CI/CD</p></li><li><p>DevOps and Observability: we will add observability to the application and use AI as the first line of support when the application breaks</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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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><p>I start with AI-native developer workflows: how to turn an idea into a specification, give coding agents the right context, and verify what they build.</p><ul><li><p><span>Part 1: </span><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 and 5: TBA</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><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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 <code>retroloop</code>.</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.</p><p>In later modules, we build on the same workflow:</p><ul><li><p>Building and shipping a full-stack app end to end: spec to frontend, backend, tests, Docker, deployment and CI/CD</p></li><li><p>Coding agent capabilities: MCP, skills, plugins, hooks and custom agents</p></li><li><p>AI for security, audit and DevOps</p></li><li><p>Taking a project of your own from an empty folder to something running</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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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><item><title><![CDATA[Six Projects That Didn’t Make It]]></title><description><![CDATA[But I learned a ton while building them]]></description><link>https://aishippingblog.com/p/six-projects-that-didnt-make-it</link><guid isPermaLink="false">https://aishippingblog.com/p/six-projects-that-didnt-make-it</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 26 Jun 2026 21:07:46 GMT</pubDate><enclosure url="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" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>I have a lot of projects that I started with some grand idea, spent time on, and then never really did anything with.</span></p><p><span>Some of them work, but I don&#8217;t need them anymore. Others don&#8217;t work at all. Some turned out to be more complex than I expected, or I couldn&#8217;t build them the way I imagined. I abandoned many of them at different stages.</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>My GitHub profile shows 195 repositories, and that&#8217;s only my personal account. I also experiment with projects in the DataTalks.Club and AI Shipping Labs organizations on GitHub. With coding agents, I write a lot more code than ever before, because I can try ideas faster. Naturally, I also create more projects that don&#8217;t go anywhere.</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_!JSAF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JSAF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 424w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 848w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 1272w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JSAF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png" width="499" height="431.0805555555556" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:933,&quot;width&quot;:1080,&quot;resizeWidth&quot;:499,&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_!JSAF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 424w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 848w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.png 1272w, https://substackcdn.com/image/fetch/$s_!JSAF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ffa7d3-f659-4896-a1f3-0e403aa84dac_1080x933.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"><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"><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"><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"><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 GitHub profile page showing 195 repositories</figcaption></figure></div><p><span>But I don&#8217;t think this is a problem.</span></p><p><span>These projects may not reach their final form, and I may not use them afterward, but building them still teaches me something. Some help me understand how to work with coding assistants. Others help me think through a vague problem in practice.</span></p><p><span>Often, I start with a problem that is not clear yet. I try one solution, build part of it, and then realize it does not quite fit. So I adjust the idea, remove some parts, try a different approach, or abandon the project entirely. The project itself may not survive, but the problem becomes clearer.</span></p><p><span>That&#8217;s why I don&#8217;t think abandoned projects are necessarily failures or a waste of time. Often, they are part of the learning process.</span></p><p><span>In this post, I&#8217;ll share:</span></p><ul><li><p><span>A few projects I started and later abandoned</span></p></li><li><p><span>Why some of them were still useful</span></p></li><li><p><span>Which ideas survived as smaller tools</span></p></li><li><p><span>What I learned from having all this dead weight in my repositories</span></p></li></ul><h2><span>Code Explainer</span></h2><p><span>In October 2025, I built a small Streamlit app to help understand other people&#8217;s repositories. I pasted in a GitHub URL, waited for it to download the main branch, and asked questions about the code.</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_!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" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BQAt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb6e334a-b051-4876-8133-83390b9d3120_1768x1086.png 424w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb6e334a-b051-4876-8133-83390b9d3120_1768x1086.png 848w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb6e334a-b051-4876-8133-83390b9d3120_1768x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb6e334a-b051-4876-8133-83390b9d3120_1768x1086.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BQAt!,w_1456,c_limit,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" width="1456" height="894" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db6e334a-b051-4876-8133-83390b9d3120_1768x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:894,&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_!BQAt!,w_424,c_limit,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 424w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_848,c_limit,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 848w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_1272,c_limit,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 1272w, https://substackcdn.com/image/fetch/$s_!BQAt!,w_1456,c_limit,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 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"><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"><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"><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"><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>Behind the scenes, an agent could list directories, read files, and grep through the codebase. The interface showed what it was looking at, which files it had analyzed, and how much the request cost. I could also ask follow-up questions without starting over.</span></p><p><span>Then I started using Claude Code and realized I didn&#8217;t need Code Explainer at all. I can just ask Claude Code, Codex, or another coding agent to explain any project. They can clone the repository, inspect the files, and answer questions directly.</span></p><p><span>So now it&#8217;s much simpler to ask a coding assistant than to maintain a separate tool for this.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/code-explainer"><span>github.com/alexeygrigorev/code-explainer</span></a><span> - a Streamlit app that loads a GitHub repository and uses an AI agent with file-reading, search, and directory-listing tools to explain it.</span></p></div><h2><span>The Fitness Tracker</span></h2><p><span>Around December 2025, when I first got a Claude Code subscription, I started building my own fitness tracker. I think everyone who tracks workouts eventually wants to build one.</span></p><p><span>It was my first project where I used Claude Code more intentionally. At that point I was trying to understand how it was different from other assistants I used previously and how much of a real project they could handle.</span></p><p><span>I already had my own fitness process. I had tried several apps and trackers, but none of them matched the way I train. Since I understood the domain and had a clear idea of what I wanted, I decided to build my own.</span></p><p><span>At first, I asked Claude Code to create both the frontend and the backend, and I did not give it many constraints. I told it to choose whatever stack it wanted. I did not like the result: it didn&#8217;t work and I didn&#8217;t understand why. So I asked it to rebuild the backend in Django and use a different frontend stack.</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_!A-xp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A-xp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 424w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 848w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 1272w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A-xp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png" width="1456" height="634" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:634,&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_!A-xp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 424w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 848w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.png 1272w, https://substackcdn.com/image/fetch/$s_!A-xp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1c6f77d-10ed-4daa-bf35-4adbb3715697_1786x778.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"><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"><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"><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"><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 wanted to define workout presets for different days, start a workout from a preset, and log the details the way I actually train: warm-ups, bodyweight exercises, drop sets, weights, and reps. I also wanted the tracker to remember the last weights I used and save unfinished workouts on the server, so I could refresh the page or switch devices without losing the session.</span></p><p><span>After that, I kept adding more features: food and meal logging, reusable meal templates, bodyweight and sleep tracking, and a metabolism view that connected these inputs. By that point, the project had a lot of custom logic, and none of it worked properly.</span></p><p><span>In the end, I gave up on it. It was my first proper Claude Code project and I built it for myself, around a domain I understood well. It did not become something I use every day, but it helped me understand how Claude Code behaves on a real project: where it helps, where it needs more guidance, and how quickly a personal tool can grow once I start adding every feature I want.</span></p><p><span>That made it easier to try other projects afterward.</span></p><div class="callout-block" data-callout="true"><p>Project: <a href="https://github.com/alexeygrigorev/fitness-tracker">github.com/alexeygrigorev/fitness-tracker</a> - a full-stack personal tracker for workout routines, active sessions, meals, weight, sleep, and metabolism data.</p></div><h2><span>The Metabolism Simulator</span></h2><p><span>In January 2026, I started </span><a href="https://alexeyondata.substack.com/p/my-experiments-with-claude-code"><span>experimenting with Claude Code</span></a><span> more seriously. I wanted to see whether I could make it keep working for a long time without coming back every few minutes to press &#8220;continue&#8221;.</span></p><p><span>I tried the </span><a href="https://github.com/anthropics/claude-code/blob/main/plugins/ralph-wiggum/README.md"><span>Ralph Wiggum plugin</span></a><span>, which prompts Claude to continue whenever it stops. </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_!txbY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!txbY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 424w, https://substackcdn.com/image/fetch/$s_!txbY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 848w, https://substackcdn.com/image/fetch/$s_!txbY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 1272w, https://substackcdn.com/image/fetch/$s_!txbY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!txbY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png" width="1158" height="459" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:459,&quot;width&quot;:1158,&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_!txbY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 424w, https://substackcdn.com/image/fetch/$s_!txbY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 848w, https://substackcdn.com/image/fetch/$s_!txbY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.png 1272w, https://substackcdn.com/image/fetch/$s_!txbY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff946ac5b-42ab-4696-8790-ac274de9db75_1158x459.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"><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"><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"><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"><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 first project I tested with this setup was a metabolism simulator. I was interested in the topic, but I was also using the project as an experiment. What happens if I let Claude Code work on the same app for many hours with a continuation loop?</span></p><p><span>Since I do sports, I&#8217;m genuinely interested in how metabolism works. What happens when I go for a run without breakfast? What happens when I eat and then go to the gym? How should I eat after a workout? What changes if I want to cut or gain muscle?</span></p><p><span>I wanted to build a simulator tailored specifically to me. It would show what happens when a person eats, trains, or sleeps: glucose, hormones, energy, and muscle recovery. Ideally, I wanted to connect it to my fitness tracker and use my own diet and training data. It would be like a personal nutrition coach that already knows my routines.</span></p><p><span>There was a computer game in the 90s called Komputerschik, or &#8220;the Computer Guy&#8221;. You had a computer and could buy more RAM or upgrade other parts. I wanted something like that, but for a human body. I wanted to change inputs and see cause and effect.</span></p><div class="image-gallery-embed" data-attrs="{&quot;gallery&quot;:{&quot;images&quot;:[{&quot;type&quot;:&quot;image/png&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7d33a0f-c534-43e5-b065-0c0287ab0a9f_457x412.png&quot;},{&quot;type&quot;:&quot;image/png&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89f18828-78c0-4278-b78d-d0506158baab_457x412.png&quot;}],&quot;caption&quot;:&quot;&quot;,&quot;alt&quot;:&quot;&quot;,&quot;staticGalleryImage&quot;:{&quot;type&quot;:&quot;image/png&quot;,&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/038738dd-0d94-4a0c-a5f8-fd84b395a494_1456x720.png&quot;}},&quot;isEditorNode&quot;:true}"></div><p><span>I asked Claude to plan the app first. Then I told it to use a client-server architecture and write frontend and backend unit tests, integration tests, and end-to-end Playwright tests. After that, I turned on the loop and let it work.</span></p><p><span>After three hours, Claude had built the interface, but the buttons did not work. I asked it to test the app through Playwright and fix the broken flows. After 20 hours, it had added many more features, but food and exercise logging still did not work.</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_!PuIm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PuIm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 424w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 848w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 1272w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PuIm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png" width="1456" height="666" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:666,&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_!PuIm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 424w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 848w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.png 1272w, https://substackcdn.com/image/fetch/$s_!PuIm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd276d3ac-d837-4ef2-88a3-544419f8c7a8_1912x874.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"><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"><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"><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"><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 turned out that instead of fixing the API problem, it just decided to display demo data, making the app look functional while the backend was broken. I had to tell it not to hide a broken backend behind a fallback.</span></p><p><span>I let Claude run for a few more days. By the end, it had created a well-designed application with a simulation loop, a dashboard, food and exercise data, hormone charts, and many other features. Not everything worked, but it was clear that Claude had produced a lot of code.</span></p><p><span>The loop can produce something that looks good, but it isn&#8217;t reliable. Claude can get sloppy and delete a test instead of fixing it. An agent can keep working forever, but it still needs steering. You need clear requirements, grooming, tests, review, and somebody to accept the work.</span></p><p><span>You need a development process.</span></p><p><span>That&#8217;s why I started working on my </span><a href="https://alexeyondata.substack.com/p/i-built-an-ai-agent-team-for-software"><span>agent team</span></a><span> in February 2026. I still use that process now. The metabolism simulator failed as a metabolism app, but it succeeded as an experiment. It showed me what breaks when I let an agent run without enough structure, and it pushed me toward the process I use now.</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_!I0uj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I0uj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 424w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 848w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 1272w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I0uj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png" width="1456" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&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_!I0uj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 424w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 848w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.png 1272w, https://substackcdn.com/image/fetch/$s_!I0uj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22945557-2639-4fc3-89e4-ef2f953454f6_2048x1016.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"><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"><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"><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"><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 agent team</figcaption></figure></div><p><span>For this concrete app, I don&#8217;t think I&#8217;ll come back to it. If I do anything in this direction, I&#8217;ll start from scratch and think more deliberately about what exactly I want to model.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/metabolism-simulator"><span>github.com/alexeygrigorev/metabolism-simulator</span></a><span> - a client-server web simulator for food, exercise, energy, hormones, and recovery.</span></p></div><h2><span>CodeHive</span></h2><p><span>I already wrote about </span><a href="https://alexeyondata.substack.com/p/i-built-an-ai-agent-team-for-software"><span>my agent team for software development</span></a><span>. </span>The team had five agents: an orchestrator, a project manager, a software engineer, a QA engineer, and an on-call engineer.</p><p><span>I tested it on five projects, but kept seeing the same problems. Claude Code could pause in the middle of a task and ask, &#8220;Shall we proceed?&#8221; when there are a lot of unfinished tasks. Or it said the work was done, even though it wasn&#8217;t.</span></p><p><span>I also couldn&#8217;t see what a subagent was doing. It could spend an hour on a task, and I had no way to tell whether it was making progress or was stuck. Sometimes the orchestrator ignored the setup entirely and sent a task straight to the SWE without PM grooming or QA verification.</span></p><p><span>The methodology lived in markdown files, so the agent could ignore any part of it. I wanted the application to enforce the pipeline instead: PM always grooms the task first, then SWE implements it, QA verifies it, and PM accepts it. The roles, acceptance criteria, and definition of done are be part of the tool, not another prompt.</span></p><p><span>At that time, I also started hitting Claude Code&#8217;s usage limits, which made me realize that relying on a single provider was a bad idea.</span></p><p><span>To deal with all of that, I started working on CodeHive. I wanted it to provide:</span></p><ul><li><p><span>Enforced PM, SWE, QA, and acceptance pipeline</span></p></li><li><p><span>Multiple agent backends, including Claude Code, Codex, GitHub Copilot, and Z.ai</span></p></li><li><p><span>A non-blocking task pool</span></p></li><li><p><span>Subagent status and intervention controls</span></p></li><li><p><span>Automatic GitHub issue intake</span></p></li><li><p><span>Access from a phone, computer, browser, and terminal</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_!55Xp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!55Xp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 424w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 848w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 1272w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!55Xp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.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;: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_!55Xp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 424w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 848w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.png 1272w, https://substackcdn.com/image/fetch/$s_!55Xp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90aaa1ea-8544-41c7-aae3-ec8a0e1da334_1792x966.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"><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"><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"><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"><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>CodeHive got too ambitious. Around that time, I discovered Termius and realized I didn&#8217;t need a complex multi-client system just to </span><a href="https://alexeyondata.substack.com/p/the-system-i-built-to-ship-code-from"><span>work from my phone</span></a><span>. Opening Termius on my phone was enough, so I no longer needed the web interface.</span></p><p><span>CodeHive wasn&#8217;t a complete waste. I worked out much of the agent-team approach there. But turning it into a tool I would actually use would take a very long time, so I stopped working on it.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/codehive"><span>github.com/alexeygrigorev/codehive</span></a><span> - multi-platform autonomous coding agent with sub-agent orchestration</span></p></div><h2><span>Litehive</span></h2><p><span>Litehive came out of CodeHive. After </span>CodeHive <span>grew too large, I pulled out the two parts I actually needed: the enforced pipeline and the ability to switch between agent engines.</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_!wmkH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wmkH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 424w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 848w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 1272w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wmkH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png" width="1456" height="807" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:807,&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_!wmkH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 424w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 848w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.png 1272w, https://substackcdn.com/image/fetch/$s_!wmkH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2a9c97-072a-4615-af21-6ff3fc1e5e11_1762x976.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"><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"><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"><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"><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>Litehive was the local-first command-line version. No interface. No web app. Each project had a small .litehive folder containing its configuration and context, while the task queue, reports, agent sessions, and runtime state were stored in SQLite outside the repository.</span></p><p><span>The hard-coded pipeline from CodeHive became Litehive&#8217;s main job. It moved each task through grooming, implementation, testing, acceptance, and a Git commit. I could queue, pause, resume, or requeue work, inspect its logs and journal, and recover a task when a runner crashed or an agent got stuck.</span></p><p><span>Each task ran in its own git worktree. When it passed the stages, Litehive committed the work, merged it into the main workspace, and pushed it.</span></p><p><span>The multiple-engine requirement also came from CodeHive. Litehive could run Codex, Claude Code, GitHub Copilot, Gemini, and OpenCode, and I could switch a task to another engine when I was approaching a limit. The skills and context were written in a format any of these agents could read.</span></p><p><span>I spent more than a month on this project and burned a huge pile of tokens. I rewrote it several times, almost from scratch, but it never became stable.</span></p><p><span>I hadn&#8217;t thought the architecture through properly, so the agents filled in the gaps. The project got complicated, and each rewrite removed some checks and edge cases from the previous version.</span></p><p><span>The state machine was the one part I really liked, because it made the system deterministic. But with the looser orchestrator I use now, I can launch many sessions at once and let it merge the results. That is much harder with a fixed state machine.</span></p><p><span>I don&#8217;t think I&#8217;ll come back to Litehive. It&#8217;s simpler for me now to start an agent and let it follow the process I have tuned for Codex and Claude Code.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/litehive"><span>github.com/alexeygrigorev/litehive</span></a></p></div><p><span>Looking back, CodeHive and Litehive were part of what helped me improve my approach to managing the agent team.</span></p><p><span>At first, I thought I needed a full system around a team of agents: roles, queues, status tracking, approvals, multiple clients, and a hard pipeline. Then CodeHive became too large. I extracted the core into Litehive. Then Litehive showed me that even the smaller version was still too rigid and too complicated for the way I actually work.</span></p><p><span>In the end, I arrived at a simpler setup: I talk to the agents directly and use a process that I have tuned through practice. That turned out to be more flexible than wrapping everything in a fixed application.</span></p><p><span>But I don&#8217;t think I would have reached that conclusion without building CodeHive and Litehive first. They helped me understand which parts of the process mattered and which parts were just extra machinery.</span></p><h2><span>What came out of Litehive</span></h2><p><span>Litehive cost me a lot of time and tokens, but two parts of it were useful enough to extract into separate projects.</span></p><h3><span>Heru</span></h3><p><span>Litehive had to run coding agents without opening their interactive chat interfaces. I wanted it to start Codex, Claude Code, GitHub Copilot, Gemini, or OpenCode from the command line, pass in a task, capture the output, and resume the same session later if needed.</span></p><p><span>It&#8217;s not that simple: all the engines have different flags, output formats, session IDs, and rules for resuming a previous run.</span></p><p><span>At first, I put all of that provider-specific logic inside Litehive. The same codebase handled the task queue and also knew how to start each CLI, which flags to pass, how to parse its output, where to find the session ID, and how to resume a previous run.</span></p><p><span>Eventually I pulled the provider-specific part into a separate tool called Heru. I kept the task queue and orchestration logic in Litehive and moved the provider-specific execution logic into Heru. Litehive decided which task should run next and which agent should handle it, and heru translated that decision into the right command for the selected provider.</span></p><p><span>I named it &#8220;Heru&#8221; because I wanted one tool to &#8220;rule them all&#8221;. </span><a href="https://tolkiengateway.net/wiki/Heru"><span>&#8220;Heru&#8221; means &#8220;Lord&#8221;</span></a><span> in one of the Elvish languages.</span></p><p><span>I used it with Claude Code and Codex, but also often with GitHub Copilot when it was cheaper. Now Copilot&#8217;s pricing makes it less useful, so I stopped relying on it. But new coding agents keep appearing, and I can easily add them to Heru.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/heru"><span>github.com/alexeygrigorev/heru</span></a><span> - a unified headless CLI that normalizes execution, events, and session resume across coding-agent CLIs.</span></p></div><h3><span>Quse</span></h3><p><span>I work on a lot of projects in parallel, so I often run out of tokens. I use Codex, Claude Code and other providers, so when I hit limits in one tool, I switch to another.</span></p><p><span>But when I&#8217;m working on a long task that I start in Claude Code, and get close to the quota, it can get interrupted at the worst possible moment. I wanted Litehive to manage that situation and switch to another provider automatically. When Claude reached 95% of its available usage, Litehive would send new tasks to Codex or Copilot instead. And when Claude is back to 0%, it can start receiving tasks again.</span></p><p><span>To make that work, I needed a unified way to check usage quotas across providers. Each provider reports usage differently, so I built a small tool called Quse to handle that logic. The name means &#8220;Quota Use&#8221;.</span></p><p><span>Quse normalizes quota checks for Codex, Claude Code, Copilot, and Z.ai. When I run it from the command line, it shows how much usage I have left for each provider and when the limit resets.</span></p><p><span>This is probably the project from this whole list that I use the most right now.</span></p><p><span>I also integrated Quse into </span><a href="https://github.com/alexeygrigorev/pocketshell"><span>PocketShell</span></a><span>, the phone shell app that I actively use now. When I approach limits, my phone sends me push notification, so I can switch to another provider.</span></p><p><span>Quse is the small part of Litehive that became a daily tool. It does not run agents on its own, but it gives me the information I need to decide whether to keep using the current provider or move the next task elsewhere.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/quse"><span>github.com/alexeygrigorev/quse</span></a><span> - normalized quota and usage checks for Codex, Claude Code, Copilot, and Z.ai.</span></p></div><h2><span>The Mermaid diagram tool</span></h2><p><span>merm, my Mermaid diagram tool, was another project where I tested the agent-team process.</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_!mGVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mGVy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 424w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 848w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mGVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png" width="1456" height="870" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:870,&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_!mGVy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 424w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 848w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!mGVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf3b8682-affb-4eeb-9119-7faa9e467d3e_1774x1060.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"><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"><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"><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"><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 built it because I wanted to render Mermaid diagrams on a server without installing Node and a headless browser just to produce an SVG. Merm is a pure Python renderer: it parses Mermaid markup, lays out the diagram, and writes SVG or PNG files.</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_!JVHG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JVHG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 424w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 848w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 1272w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JVHG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png" width="1456" height="1149" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1149,&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_!JVHG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 424w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 848w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.png 1272w, https://substackcdn.com/image/fetch/$s_!JVHG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02698bd8-0b05-49de-b305-f52865339f9f_1456x1149.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"><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"><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"><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"><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 href="https://github.com/alexeygrigorev/merm/tree/main/docs/examples#mermaid-readme-6">Sequence diagram</a> with three participants, a loop fragment, and notes rendered by merm</figcaption></figure></div><p><span>It has a command-line interface, so I can render a .mmd file or pipe Mermaid text into it. I started with flowcharts, then expanded the renderer to support sequence, class, state, ER, Gantt, pie, mindmap, and git graph diagrams.</span></p><p><span>I polished this project to a good state. I did not spend much of my own time on it, though it probably used a lot of tokens. More importantly, I used the project to test and refine the agent-team process on a real codebase.</span></p><p><span>The tool works, but I do not have a strong need for it right now. Most of the time I need to render diagrams in a browser, so I simply use the </span><a href="https://github.com/mermaid-js/mermaid"><span>mermaid.js library</span></a><span>. </span></p><p><span>Maybe I will come back to Merm later if I build a more dedicated workflow for diagrams. For now, this project is not exactly abandoned. It works. I just do not need it enough to use it regularly.</span></p><div class="callout-block" data-callout="true"><p><span>Project: </span><a href="https://github.com/alexeygrigorev/merm"><span>github.com/alexeygrigorev/merm</span></a></p></div><h2><span>Cleaning up the Dead Weight</span></h2><p><span>Abandoning some projects along the way is a normal part of the experimentation process. Not everything you build has to become useful,.</span></p><p><span>A lot of projects die. Some just sit on GitHub and never get touched again. And that&#8217;s fine.</span></p><p><span>The valuable part is what you learn from doing the project, or the process you developed while building it. Sometimes it&#8217;s a smaller tool that splits off from it. Sometimes it&#8217;s simply the realization that you should not solve this problem in this particular way.</span></p><p><span>For me, if one out of ten projects actually sticks and becomes part of my real workflow, I would already consider that a success.</span></p><p><span>The important thing is to keep experimenting, notice when something is not working, stop when it is no longer useful, and move on with a better understanding than you had before.</span></p><p><span>And while preparing this article, I decided to clean up some of my repositories and delete those I no longer needed. There are probably still a lot left that I don&#8217;t need, and I&#8217;ll likely delete those later too.</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_!SVB5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SVB5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 424w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 848w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 1272w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SVB5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png" width="513" height="305.9" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:644,&quot;width&quot;:1080,&quot;resizeWidth&quot;:513,&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_!SVB5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 424w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 848w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.png 1272w, https://substackcdn.com/image/fetch/$s_!SVB5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf0a7a51-f0d3-4173-81d8-7b36fbcd7db8_1080x644.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"><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"><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"><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"><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 a cleanup pass: down to 168 repositories</figcaption></figure></div><h2><span>What I&#8217;ve Been Working on Recently</span></h2><h3>1) Updated the AI Engineering Field Guide</h3><p>I continued working on the <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineering Field Guide</a>, a data-driven resource about AI engineering roles, skills, interviews, and career paths.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qwsC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qwsC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 424w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 848w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 1272w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qwsC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png" width="621" height="434.125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:755,&quot;width&quot;:1080,&quot;resizeWidth&quot;:621,&quot;bytes&quot;:504218,&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/203724093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.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_!qwsC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 424w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 848w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.png 1272w, https://substackcdn.com/image/fetch/$s_!qwsC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f458197-75c6-4d7a-a0cb-1261e90ea022_1080x755.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"><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"><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"><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"><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 week, I refreshed the job data behind the guide and updated the repo. It now draws on 4,894 real job descriptions, interview experiences, and practitioner stories. The guide covers role analysis, skills, responsibilities, use cases, interview preparation, company-by-company interview processes, learning paths, and portfolio strategy.</p><h3>2) Vercel FAQ Agent workshop</h3><p>I presented the <a href="https://aishippinglabs.com/workshops/vercel-faq-agent">Vercel FAQ Agent workshop</a> at AI Shipping Labs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PC_I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PC_I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 424w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 848w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PC_I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg" width="637" height="334.425" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:637,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cover image for Vercel FAQ 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="Cover image for Vercel FAQ Agent" title="Cover image for Vercel FAQ Agent" srcset="https://substackcdn.com/image/fetch/$s_!PC_I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 424w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 848w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!PC_I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d11307a-6dba-4d35-8d35-cdccb17db8c6_1200x630.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"><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"><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"><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"><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 take the FAQ agent from earlier workshops and ship it on Vercel. Now a Next.js app serves the UI, a serverless function runs the agent loop, and the chat model is reached through the Vercel AI Gateway.</p><p>The workshop compares four ways to build the same agent on Vercel: AI SDK with MiniSearch, <a href="https://vercel.com/eve">Eve</a> with durable sessions, Eve with Upstash Vector, and a Python FastAPI backend for teams that prefer Python.</p><p>I was really impressed with eve and how easy it is to deploy agents with Vercel. But there&#8217;s one downside: it&#8217;s all in TypeScript. </p><h3>3) Full-Stack Vibe Coding workshop</h3><p>I also worked on the Full-Stack Vibe Coding workshop. It was an offline workshop, but I made the materials available for free <a href="https://aishippinglabs.com/workshops/full-stack-vibe-coding">on the AI Shipping Labs website</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_!-XMt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-XMt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-XMt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg" width="1280" height="960" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:960,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;No alternative text description for this image&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="No alternative text description for this image" title="No alternative text description for this image" srcset="https://substackcdn.com/image/fetch/$s_!-XMt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-XMt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9020db93-70cd-46aa-a31f-b105af2ddb4e_1280x960.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"><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"><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"><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"><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 workshop builds Snake as a full-stack web app end-to-end, with a coding assistant doing most of the work. We start with a React frontend generated in Lovable, write an OpenAPI spec that the frontend and backend agree on, then use a coding assistant to build a FastAPI backend from that spec.</p><p>After that, we add a real database, connect the frontend to the backend, run tests, containerize the app with Docker Compose, deploy it to AWS with infrastructure as code, and add a GitHub Actions CI/CD pipeline.</p><p>The point is to show how far you can get with a coding assistant, even if you do not have deep frontend, backend, or DevOps experience, as long as you can guide the work and review what the assistant produces.</p><h2><span>Interesting Tools</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_!jj1N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jj1N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 424w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 848w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1272w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png" width="1456" height="1140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1140,&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_!jj1N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 424w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 848w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1272w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.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"><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"><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"><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"><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/omnigent-ai/omnigent"><span>Omnigent</span></a><span> is an open-source meta-harness from Databricks for coordinating coding agents. Instead of relying on one agent, you can run several agents in one session, for example, one agent writes code and another reviews it. Omnigent supports Claude Code, Codex, Pi, the OpenAI Agents SDK, and YAML-defined agents. It also moves some controls out of prompts and into the harness itself: spend caps, approval gates, sandboxed tool execution, and an egress proxy that can inject secrets without exposing them to the agent. The project is released under Apache 2.0.</span></p></li><li><p><a href="https://github.com/run-llama/liteparse">LiteParse</a> is a fast open-source document parser from the LlamaIndex team. It runs locally, so you can parse documents without cloud dependencies or proprietary LLM features. It supports PDF, DOCX, XLSX, PPTX, and images; uses PDFium for spatial text parsing with bounding boxes; and can run OCR through bundled Tesseract or an HTTP OCR server. The Rust core has bindings for Python, Node.js/TypeScript, the browser through WASM, and a CLI, so it should fit into most local document-processing pipelines.</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[The System I Built for AWS Access Without Keys]]></title><description><![CDATA[I sometimes run offline workshops.]]></description><link>https://aishippingblog.com/p/the-system-i-built-for-aws-access</link><guid isPermaLink="false">https://aishippingblog.com/p/the-system-i-built-for-aws-access</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 19 Jun 2026 16:50:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FnCR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>I sometimes run offline workshops. These workshops require participants to have access to cloud resources such as AWS. This is okay when people come prepared, but often it&#8217;s not the case.</span></p><p><span>This happened when Exasol, a database company, asked me to run a workshop for them. They released a new version of their database, Exasol Personal. It&#8217;s normally a paid service, but this edition runs in your own AWS account. You need an account and a few permissions, then you can create a cluster and use it from your laptop.</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_!FnCR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FnCR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 424w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 848w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 1272w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FnCR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png" width="1798" height="676" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:676,&quot;width&quot;:1798,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:56698,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd6a55f3-2921-4dbc-bc27-633d3d9fa02f_1798x992.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_!FnCR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 424w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 848w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.png 1272w, https://substackcdn.com/image/fetch/$s_!FnCR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf8b3925-5ffa-4a56-a2fa-421b8f27d1b8_1798x676.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"><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"><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"><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"><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 me, it was very easy to set it up. But then I started thinking about how to make it scale to 50-60 workshop participants, who will most likely be unprepared.</span></p><p><span>So I needed to find a way for the participants to provision resources in my AWS account without giving them my AWS keys.</span></p><p><span>The solution I found turned out to be useful not only for workshops, but also for coding agents. I don&#8217;t want to give my agents permanent access to my main AWS account. Instead, I want to restrict them to a sandbox environment and decide when and for how long they can have access.</span></p><div class="callout-block" data-callout="true"><p><span>This article is based on the workshop that I ran at the Berlin AWS Group meetup. Watch the </span><a href="https://youtu.be/bScTPc0RnXU?si=R5KDk1Ld6p-QiE_R"><span>recording</span></a><span> below and check out the </span><a href="https://docs.google.com/presentation/d/1wrH2we0J4atE3Dt2afbhygyoyATCLybGIBdfj84IMYM/edit"><span>slides</span></a><span> from this talk.</span></p><div id="youtube2-bScTPc0RnXU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;bScTPc0RnXU&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/bScTPc0RnXU?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><h2><span>The workshop problem</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_!1OrG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1OrG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 424w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 848w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 1272w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1OrG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png" width="1788" height="544" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:544,&quot;width&quot;:1788,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:94972,&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;:false,&quot;internalRedirect&quot;:&quot;https://alexeyondata.substack.com/i/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0e53fa-4c13-4cde-91b6-20eb29f8760f_1788x996.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_!1OrG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 424w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 848w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 1272w, https://substackcdn.com/image/fetch/$s_!1OrG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9407f6de-cf55-4cb1-b55a-b879745a6722_1788x544.png 1456w" sizes="100vw"></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"><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"><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"><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"><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 data-color="rgb(68, 71, 70)" style="color: rgb(68, 71, 70);">I needed to find a way to give 50 or 60 people in a room access to provision AWS resources from their own machines</span><span>, with nothing to install and no keys to copy. You sit down, open the workshop environment, and it works.</span></p><p><span>I didn&#8217;t want to give the participants my AWS key. First, it&#8217;s a very bad idea for security reasons. Second, how do I even do 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_!TbEt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TbEt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 424w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 848w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 1272w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TbEt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png" width="1802" height="676" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:676,&quot;width&quot;:1802,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69594,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07d22851-c298-4ac4-ab30-979410866a2f_1802x996.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_!TbEt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 424w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 848w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.png 1272w, https://substackcdn.com/image/fetch/$s_!TbEt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bc3e6cc-da88-43f2-9210-7807b0fa2f6c_1802x676.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"><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"><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"><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"><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>Type it on the screen, send it by email, drop it in a repo for a few minutes? Once a key is out, anyone who has it can provision whatever they want until I disable it. If somebody starts mining Bitcoin on my account, that&#8217;s my problem.</span></p><p><span>I also didn&#8217;t want to use my main AWS account. I&#8217;m not a security expert, and with 50+ people I don&#8217;t know (and don&#8217;t necessarily trust), things may go wrong.</span></p><p><span>And finally, I wanted to run in GitHub Codespaces. It&#8217;s very convenient, and I use it for all my workshops. You click a button and get a remote machine with everything installed and the same environment as everyone else. I didn&#8217;t know if it&#8217;d be possible for this workshop, but I wanted to find an equally convenient option.</span></p><h2><span>EC2 instance profiles</span></h2><p><span>The first thing I thought about was EC2 instance profiles.</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_!rUJD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rUJD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 424w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 848w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 1272w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rUJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png" width="1802" height="786" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:786,&quot;width&quot;:1802,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75403,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7f0aa0d-5ad6-47a5-a401-ab7e6d85dca1_1802x1000.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_!rUJD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 424w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 848w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.png 1272w, https://substackcdn.com/image/fetch/$s_!rUJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6f0ad3-e63d-4ddd-93e2-8de605f76d99_1802x786.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"><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"><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"><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"><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>You attach a role to an instance, and when you start it, it gets access to all the resources you configured. You don&#8217;t need to worry about managing keys: EC2 does it for you.</span></p><p><span>But to create instances with the profiles, I&#8217;d need to distribute my key to the participants again, who would then use it to provision the instances.</span></p><p><span>So I wanted something that&#8217;s like EC2 instance profiles, but it would work without having to share my keys, and ideally outside of AWS &#8211; on GitHub Codespaces.</span></p><p><span>I needed to reproduce the way instance profiles work. The flow in EC2 looks like this:</span></p><ol><li><p><span>You launch an EC2 instance with a role attached</span></p></li><li><p><span>EC2 calls STS and assumes that role</span></p></li><li><p><span>STS returns temporary credentials</span></p></li><li><p><span>Those credentials become available through the instance metadata service</span></p></li><li><p><span>The AWS CLI, boto3, and other SDKs know where to find them</span></p></li><li><p><span>When the credentials expire, AWS refreshes them automatically</span></p></li></ol><p><span>If I could do something similar, but for Codespaces, it&#8217;d solve my problem: when the workshop is over, I just deactivate the profile, and my key is never used.</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_!cVpp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cVpp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 424w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 848w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cVpp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png" width="1456" height="811" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:811,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:298111,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.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_!cVpp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 424w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 848w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!cVpp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba0a7e00-3044-4c4c-9c3b-c2d7dec37d77_1802x1004.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"><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"><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"><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"><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>What I built: a credential endpoint I host</span></h2><p><span>I started looking for alternatives and found this environment variable:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;a153d023-1711-427a-922e-69b38f364058&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">AWS_CONTAINER_CREDENTIALS_FULL_URI</code></pre></div><p><span>It tells the AWS SDK where to fetch credentials. It is typically used in container environments, but it also works outside them. So I can point it to an HTTP URL and run a small service behind it. The service needs to create temporary credentials, and when they expire, the AWS SDK automatically asks for a refresh.</span></p><p><span>In my case, I created a Lambda that assumes an AWS role and returns temporary credentials in the format the SDK expects.</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_!uWUS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uWUS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 424w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 848w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uWUS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png" width="1456" height="811" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:811,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131063,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.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_!uWUS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 424w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 848w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!uWUS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cca88c7-44a4-4749-abd9-191c34e31ca5_1796x1000.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"><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"><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"><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"><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&#8217;s the same flow as instance profiles, but I provide the endpoint instead of AWS doing it via the metadata service.</span></p><p><span>It moves the mechanism off EC2 and lets it run anywhere the SDK runs, including a Codespace.</span></p><h2><span>Wiring it into Codespaces</span></h2><p><span>I didn&#8217;t want this anywhere near my personal account. So I created a separate AWS sandbox account, put the Lambda there, and built a repository template for the workshop. The template carried a dev container, so when participants forked the repo and opened it in Codespaces, they got the right tools and the credential URL already configured. They opened a Codespace, ran the workshop commands, and the SDK pulled credentials from the Lambda. They never had to know the Lambda existed.</span></p><p><span>Second, the repo was public, so anyone with the URL could find the credential endpoint, and I didn&#8217;t want the Lambda handing out credentials to the whole internet. So I added a secret check and shared the secret offline during the workshop. I wrote it on the screen. I think it was something funny, like </span><span data-color="rgb(163, 21, 21)" style="color: rgb(163, 21, 21);">bananas</span><span>.</span></p><p><span>You can check the code for this on GitHub:</span></p><ol><li><p><a href="https://github.com/alexeygrigorev/aws-workshop-credentials"><span>aws-workshop-credentials</span></a><span> for the credential-vending Lambda.</span></p></li><li><p><a href="https://github.com/alexeygrigorev/exasol-workshop-starter"><span>exasol-workshop-starter</span></a><span> for the dev-container template.</span></p></li></ol><h2><span>The same problem with coding agents</span></h2><p><span>The same pattern helps with coding agents.</span></p><p><span>Maybe you use Claude Code or Codex too, and sometimes they need AWS. I was running them on my laptop, where I also kept admin AWS credentials. And I am guilty of running them in skip-permissions mode, where the agent doesn&#8217;t ask before reading a file, running a command, or changing something. Approving every small step is annoying, so I told it to do whatever it wanted.</span></p><p><span>What could go wrong?</span></p><p><span>Plenty. One of the agents dropped my production database. I wrote about that here: </span><a href="https://alexeyondata.substack.com/p/how-i-dropped-our-production-database"><span>How I Dropped Our Production Database and Now Pay 10% More for AWS</span></a><span>. When you delete an RDS instance, the backups go with it. I thought I had daily backups to fall back on. I didn&#8217;t. I opened support requests, couldn&#8217;t recover anything, and recovery only became possible after I upgraded to business support.</span></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;0dd38c4a-e24e-4599-bd1b-7f5f82f5d0a9&quot;,&quot;caption&quot;:&quot;I&#8217;m working on expanding the AI Shipping Labs website and wanted to migrate its current version from static GitHub Pages to AWS. And later, replace the original Next.js setup with a Django version.&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 I Dropped Our Production Database and Now Pay 10% More for AWS&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-03-06T08:01:59.490Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ropw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc7921dd-b81d-453a-b832-7c8670a9fbeb_1600x880.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/how-i-dropped-our-production-database&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189989144,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:166,&quot;comment_count&quot;:34,&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>This post isn&#8217;t about that incident. But that incident is the reason I now think about how agents get AWS access at all. The rule I took from it: an agent should never have a path to production.</span></p><p><span>My first step was to move agents to a remote sandbox server. If an agent decides to delete something, it can&#8217;t do that because it lacks access. The server itself is disposable. I can easily recreate it, so if an agent breaks it, that&#8217;s fine.</span></p><p><span>Second, real deployments go through CI/CD. When I need to apply a Terraform change, I let the agents write the files and push them to GitHub, then I apply the change from my laptop. On my laptop for infra work, I don&#8217;t use skip-permissions mode, so I stay in control of anything that touches real infrastructure.</span></p><p><span>But sometimes I want my agents to experiment with infra on AWS. It happens when I need to work out how to deploy a new service, which resources it needs, and which permissions it requires. For this, I created a separate sandbox AWS account. When agents need access to it, I run a script that writes a credential file into the project folder the agent is working in, and the agent gets access for about an hour. The tool is </span><a href="https://github.com/alexeygrigorev/aws-sandbox-cli"><span>aws-sandbox-cli</span></a><span>.</span></p><p><span>The result is that agents have enough room to explore and break things, but only within the sandbox, and never on a path that reaches production.</span></p><h2><span>Turning access on from my phone</span></h2><p><span>But sometimes I&#8217;m not at my laptop, I&#8217;m on my phone, and I still want agents to work. I travel often, and now and then I want to let an agent figure out some inference work </span><a href="https://alexeyondata.substack.com/p/the-system-i-built-to-ship-code-from"><span>while I&#8217;m away from my desk</span></a><span>.</span></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d488f33b-6a04-4eea-9fc1-4897e98ac759&quot;,&quot;caption&quot;:&quot;Lately, I&#8217;ve been to three conferences in a row: Darmstadt, Amsterdam, and Porto. The work doesn&#8217;t slow down for any of that, so I have to keep things moving.&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;The System I Built to Ship Code From a Phone&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-05-22T13:51:36.765Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!-mWd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/the-system-i-built-to-ship-code-from&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:198835715,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:7,&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><p><span>I don&#8217;t have admin AWS credentials on my phone, and I don&#8217;t want them there. Reaching my laptop instead isn&#8217;t an option either. When I travel, it&#8217;s in my backpack, and when I&#8217;m out for the day, it&#8217;s at home behind a router, so SSHing into it isn&#8217;t trivial. And even when I can access the sandbox, I don&#8217;t want agents hitting AWS at will. They might provision ten GPU instances, and then I have a very expensive problem.</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_!n25z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n25z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 424w, https://substackcdn.com/image/fetch/$s_!n25z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 848w, https://substackcdn.com/image/fetch/$s_!n25z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!n25z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n25z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png" width="1456" height="809" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.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;:181678,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.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_!n25z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 424w, https://substackcdn.com/image/fetch/$s_!n25z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 848w, https://substackcdn.com/image/fetch/$s_!n25z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!n25z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F694f3f6f-0bc7-44ae-8799-dc8df46d023f_1808x1004.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"><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"><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"><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"><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>So I reused the credential URL. The remote sandbox already points its credential URI at a Lambda, so I built a phone app that toggles that Lambda on and off. When the toggle is on, the Lambda hands out credentials. When it&#8217;s off, the sandbox asks and fails. The access is temporary, and I can hold it for an hour. When I need an agent to run an experiment, I open the app, flip the toggle on, and flip it off when I&#8217;m done. The tool is </span><a href="https://github.com/alexeygrigorev/phone-aws-gate"><span>phone-aws-gate</span></a><span>. I connect to the sandbox from the phone with Termius, an SSH client for phones.</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_!YBEl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YBEl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 424w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 848w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YBEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png" width="1456" height="811" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:811,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:134920,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.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_!YBEl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 424w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 848w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.png 1272w, https://substackcdn.com/image/fetch/$s_!YBEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a957c24-beac-4606-9885-aca1a4ba03a6_1802x1004.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"><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"><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"><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"><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">caption...</figcaption></figure></div><p><span>For example, when I connect to the remote server and run:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;8968646f-010b-4c7a-9613-2769a1cecbe5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">aws sts get-caller-identity</code></pre></div><p><span>It fails because there is no AWS access. I flip the toggle on my phone; it asks for my fingerprint and grants access for 15 minutes, with a timer running. Now </span><code>get-caller-identity</code><span> works. I flip the toggle off, and the access is gone.</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_!4MZP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4MZP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 424w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 848w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 1272w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4MZP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png" width="1456" height="812" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:812,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:130041,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.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_!4MZP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 424w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 848w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.png 1272w, https://substackcdn.com/image/fetch/$s_!4MZP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae0a96c0-ddbc-48f3-b948-6c5678cd14aa_1790x998.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"><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"><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"><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"><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>Where I use it now</span></h2><p><span>It started as a workshop problem: how do I let 50 or 60 people use AWS without handing out long-lived keys? The answer was a credential endpoint I control. The same endpoint now does two more jobs. It gives coding agents temporary access to a sandbox account and sits behind a toggle on my phone, so I can turn that access on and off from anywhere.</span></p><p><span>One mechanism covered all three because the underlying need was the same each time: temporary, revocable AWS access with no long-lived keys sitting on a machine.</span></p><p><span>I run the credential endpoint now, so its security is mine to get right, and the access is only as locked down as I make it. The workshop ran behind a word I wrote on a screen. That was a deliberate trade for a setup I could throw away the same day, not a pattern I&#8217;d reuse for anything that had to last.</span></p><p><span>The rule I follow now is the one the dropped database taught me. Before anything gets AWS access, whether it&#8217;s a room full of strangers, an agent, or a script, I ask the same questions. Is the access scoped to a sandbox? Is it temporary? Can I revoke it? And can it reach production? If the answer to that last one is yes, I stop and fix it first.</span></p><h2><span>What I&#8217;ve been working on this week</span></h2><h3>Sprint prep with Codex</h3><p><span>I spent a lot of the week getting </span><a href="https://aishippinglabs.com/"><span>AI Shipping Labs</span></a><span> ready for the </span><a href="https://aishippinglabs.com/events/groups/july-2026-community-sprint"><span>next sprint</span></a><span>. Codex ran for over two days on platform features, and I gave it a lot of feedback along the way. Most of it was fixes and polish of existing features, making what already exists look and work better. It kept going until everything on my list was done.</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_!xIQl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xIQl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 424w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 848w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 1272w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xIQl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png" width="489" height="474.9638888888889" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1049,&quot;width&quot;:1080,&quot;resizeWidth&quot;:489,&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_!xIQl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 424w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 848w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.png 1272w, https://substackcdn.com/image/fetch/$s_!xIQl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e3b4c14-35fe-4857-81e8-2c5ccd65d2f9_1080x1049.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"><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"><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"><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"><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>Codex (gpt-5.5) reporting the backlog goal complete: the shipped fixes, around 9.5M tokens, and 2d 11h 39m elapsed</span></figcaption></figure></div><h3>Cloudflare Workers workshop</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iyhj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iyhj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 424w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 848w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 1272w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iyhj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png" width="448" height="340.3410852713178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:980,&quot;width&quot;:1290,&quot;resizeWidth&quot;:448,&quot;bytes&quot;:107375,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.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_!iyhj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 424w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 848w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.png 1272w, https://substackcdn.com/image/fetch/$s_!iyhj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe053fd-bdf5-4691-8da6-67da816b8762_1290x980.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"><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"><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"><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"><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>On Wednesday, we ran an AI Shipping Labs <a href="https://aishippinglabs.com/workshops/2026-06-17-cloudflare-workers-vectorize-agent">workshop on Cloudflare Workers</a>, and it was more interesting than I expected. The idea was to take the FAQ agent from our earlier deployment workshops and move its whole online path onto Cloudflare.</p><p>The question we wanted to answer was whether Cloudflare could replace the usual stack for a small FAQ agent: a FastAPI server, a separate vector database, and a paid always-on host.</p><p>It mostly can. Cloudflare runs the full online path, with one caveat: ingestion still runs locally as an operator command rather than as a public endpoint, so you load the FAQ into Vectorize yourself when you need to.</p><p>We built that path end-to-end, creating credentials and a Vectorize index, ingesting the FAQ, running the Worker locally against the real Cloudflare services, then deploying it and cleaning it up. We also tried a Python Worker rewrite. Workers are still cheap, and for small pet projects, they're close to ideal.</p><div class="callout-block" data-callout="true"><p><span data-color="rgb(54, 55, 55)" style="color: rgb(54, 55, 55);">If you want the notes from this workshop, </span><a href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_content=2026_05_22">join AI Shipping Labs</a><span data-color="rgb(54, 55, 55)" style="color: rgb(54, 55, 55);">. The Basic plan includes notes, and the Main plan includes recordings.</span></p></div><h3>A lighter FAQ assistant, and zerosearch</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-WEf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-WEf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 424w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 848w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 1272w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-WEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png" width="1456" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&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_!-WEf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 424w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 848w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.png 1272w, https://substackcdn.com/image/fetch/$s_!-WEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78967d0f-0376-4c34-9fac-54ddc4c6ba47_1772x818.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"><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"><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"><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"><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>After that workshop, I tried to port the <a href="https://github.com/DataTalksClub/faq-assistant">DataTalks.Club FAQ assistant</a> to Cloudflare Workers and make it as small as possible. The Python layer on Workers doesn&#8217;t support extra libraries, so I rewrote MinSearch as a zero-dependency, pure-Python library called <a href="https://github.com/alexeygrigorev/zerosearch">zerosearch</a>.</p><h3>A shared email service for the course management platform</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lut6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lut6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 424w, https://substackcdn.com/image/fetch/$s_!lut6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 848w, https://substackcdn.com/image/fetch/$s_!lut6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 1272w, https://substackcdn.com/image/fetch/$s_!lut6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lut6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png" width="1456" height="784" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.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;: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_!lut6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 424w, https://substackcdn.com/image/fetch/$s_!lut6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 848w, https://substackcdn.com/image/fetch/$s_!lut6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.png 1272w, https://substackcdn.com/image/fetch/$s_!lut6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75dcc301-0b4e-483a-8fd4-41fd19640e0b_1782x960.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"><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"><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"><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"><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 started wiring email into the <a href="https://courses.datatalks.club/">course management platform</a>, which wasn&#8217;t possible before. It uses Amazon SES to send mail tailored to our setup, starting with the course management platform.</p><p>Emails already work in AI Shipping Labs, which I finished a few weeks ago, but I want to pull the logic into a separate service, <a href="https://github.com/DataTalksClub/datamailer">DataMailer</a>, so all our platforms can share it. My current goal is to get the course management platform emails working first, then move the AI Shipping Labs emails over, and maybe the weekly emails after that. If it ends up replacing MailChimp, great. If not, that&#8217;s fine, and we keep MailChimp. But that&#8217;s the direction.</p><div class="callout-block" data-callout="true"><p>I plan to write more about DataMailer in one of my future newsletters. Subscribe to stay updated!</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><span>Interesting Tools</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_!jj1N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jj1N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 424w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 848w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1272w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png" width="1456" height="1140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1140,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1024035,&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/202705654?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.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_!jj1N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 424w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 848w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.png 1272w, https://substackcdn.com/image/fetch/$s_!jj1N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7413762a-2f58-4c5a-81ba-ff3f9f59fff4_1706x1336.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"><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"><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"><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"><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/omnigent-ai/omnigent"><span>Omnigent</span></a><span> is an open-source meta-harness from Databricks. It sits above the coding agents you already use (Claude Code, Codex, Pi, the OpenAI Agents SDK, or your own YAML-defined agents) and makes them work together as one system. You can run several agents in a single session, for example, one vendor&#8217;s agent reviewing another&#8217;s code. Policies like spend caps and approval gates are enforced at the harness layer instead of through prompts, and tools run inside an OS sandbox with an egress proxy that injects secrets the agent never sees. Sessions follow you across the terminal, web, mobile, and a macOS app, and you can share a live one by URL so teammates can watch or co-drive. It&#8217;s released under Apache 2.0.</span></p></li><li><p><a href="https://github.com/DietrichGebert/ponytail"><span>Ponytail</span></a><span> is an AI agent skill that pushes coding agents to write the least code that does the job, what its author calls &#8220;the laziest senior dev in the room,&#8221; replacing fifty lines with one. Before writing anything, the agent works down a ladder: does this need to exist, does the standard library or a native platform feature already cover it, can it be done in one line? It builds only the minimum that works, but never cuts validation, error handling, security, or accessibility. It installs as a plugin for Claude Code, Codex, and a dozen other agents. On agentic benchmarks, it cut code by around 54% while staying cheaper and faster than the unassisted baseline.</span></p></li></ul><h2>Resources</h2><div id="youtube2-MdZLf6C5rW8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;MdZLf6C5rW8&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/MdZLf6C5rW8?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>Ivan Brigida ran a <a href="https://www.youtube.com/live/MdZLf6C5rW8?si=76oYuKJC5u0DkqKn">workshop on building a Stock Research Agent</a>. He had clearly prepared a lot, and I liked how he presented it.</p><div><hr></div><p><span data-color="rgb(54, 55, 55)" style="color: rgb(54, 55, 55);">Edited by </span><a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[AI Design for Non-Designers]]></title><description><![CDATA[My workflow for AI-generated interfaces: how I use mockups and design guidelines to avoid AI slop]]></description><link>https://aishippingblog.com/p/ai-design-for-non-designers</link><guid isPermaLink="false">https://aishippingblog.com/p/ai-design-for-non-designers</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 12 Jun 2026 08:06:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zJM4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You can usually tell when a website was created by AI. It often happens with &#8220;one-shot designs&#8221;: you ask a coding assistant to create a website, open the result, and immediately see that it is AI-generated.</p><p>A lot of the time, that is completely fine. For a simple website, one-shot is more than enough, and it&#8217;s way better than what I could ever design myself.</p><p>But I don&#8217;t want my websites to look too generic. There are thousands of web pages now that all look the same. In writing, we have markers like &#8220;delve&#8221; or excessive bold formatting that give away AI-generated text. In design, there are similar elements you notice immediately.</p><p>One-shot designs also get hard to use. They tend to grow overly complex, with elements not always where I&#8217;d expect them. Each new page brings elements that don&#8217;t match the rest. Eventually, the whole thing becomes messy.</p><p>I am not a designer, and I never liked front-end or design work. But I build a lot of user-facing tools now, both web and mobile. I want them to look nice and clean, with elements placed logically.</p><h2>One-Shot AI Designs</h2><p>When you look at AI-generated designs, the first thing you notice is the feature grid: cards with an icon, a heading, a short description, and a colored accent. I find it very generic and often boring. And these left-border accents give away AI designs immediately.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zJM4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zJM4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 424w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 848w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 1272w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zJM4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png" width="1280" height="547" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:547,&quot;width&quot;:1280,&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_!zJM4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 424w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 848w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.png 1272w, https://substackcdn.com/image/fetch/$s_!zJM4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc09ad9c0-1b5a-48f9-9ca8-8401db048ea1_1280x547.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"><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"><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"><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"><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 usual icon-title-blurb grid, with a colored accent on each card.</figcaption></figure></div><p>When you ask AI to implement something more complicated than a landing page, it tends to cram elements together. For admin panel interfaces where you want to perform some actions, AI will put a lot of buttons together to the right from the title. I always have to correct it and ask to put the buttons under the title.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TV-u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TV-u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 424w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 848w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 1272w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TV-u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png" width="1280" height="458" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:458,&quot;width&quot;:1280,&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_!TV-u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 424w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 848w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.png 1272w, https://substackcdn.com/image/fetch/$s_!TV-u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3007c77-db89-41b2-aed4-51702a28af5e_1280x458.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"><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"><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"><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"><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">All actions are pushed into the top-right corner, so the header becomes too busy.</figcaption></figure></div><p>Also, coding agents like columns. Agents split forms from metadata panels, or put metrics, tables, and settings panels at the same visual level. The page becomes quite busy quickly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tfu-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tfu-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 424w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 848w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 1272w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tfu-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png" width="1280" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1280,&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_!tfu-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 424w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 848w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.png 1272w, https://substackcdn.com/image/fetch/$s_!tfu-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dff7b67-6cd9-4a83-92c9-1d370009aaaf_1280x752.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"><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"><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"><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"><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 event editor splits the form and metadata into separate columns.</figcaption></figure></div><p>I can see these patterns repeated across different projects with both Codex and Claude producing similar designs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HTpz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HTpz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 424w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 848w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 1272w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HTpz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png" width="1280" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:1280,&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_!HTpz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 424w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 848w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.png 1272w, https://substackcdn.com/image/fetch/$s_!HTpz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67489add-8e22-4c7f-8334-dafe689b1c59_1280x923.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"><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"><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"><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"><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 course participant page combines metric cards, a table, and side panels, which makes the page feel crowded.</figcaption></figure></div><p></p><h2>My Own Designs</h2><p>While you can notice AI-generated patterns, my own work is by no means better. I&#8217;m not a designer and always wanted to stay as far away from front-end work as possible.</p><p>But I still needed to create web pages, like the main DataTalks.Club website that I created back in 2020. I used the <a href="https://getbootstrap.com/">Bootstrap</a> framework because it was something I was already familiar with, so it was easier for me to build something on top of 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_!yEvX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yEvX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 424w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 848w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yEvX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png" width="1456" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&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_!yEvX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 424w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 848w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!yEvX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64435e34-14e9-460c-bc10-a7eb28db898f_2048x1050.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"><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"><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"><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"><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 main DataTalks.Club website I built in 2020.</figcaption></figure></div><p>It has a headline, a signup form, a few illustrations, and a lot of empty space. It did the job, but you can clearly see that I&#8217;m not a front-end engineer.</p><p>Another example is the <a href="https://courses.datatalks.club/">Course Management Platform</a>. This is the platform we use at DataTalks.Club to run courses. For each course we have homework and project submission forms, a leaderboard and a few other useful things.</p><p>Previously I had to manage everything via Google Forms and Google Spreadsheets that were stitched together with a bunch of Python scripts. I started working on automating all that in 2023, and again chose Bootstrap as the CSS framework. I ran the first cohort of the Data Engineering Zoomcamp with it in 2024 and it has been saving so much of my time since then.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uYKr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uYKr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 424w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 848w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 1272w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uYKr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png" width="1456" height="673" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:673,&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_!uYKr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 424w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 848w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.png 1272w, https://substackcdn.com/image/fetch/$s_!uYKr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcb7ced3-8d01-4148-99b7-19c3ddcf30c9_1466x678.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"><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"><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"><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"><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 first version of the course management platform (<a href="https://web.archive.org/web/20240420184858/http://courses.datatalks.club/">Wayback Machine</a>). A functional Bootstrap page with a list of courses.</figcaption></figure></div><p>For a long time that was fine. The platform did its job, and styling was never the priority for me.</p><h2>Migrating the Course Platform to Tailwind in Codex</h2><p>In November 2024, one of the course participants <a href="http://github.com/DataTalksClub/course-management-platform/issues/76">filed an issue to migrate the course platform to Tailwind</a>.</p><p>I had no time for it, and I knew nothing about Tailwind, so I didn&#8217;t implement it. There were attempts to redesign the website from other contributors, but I didn&#8217;t like the outcome, so none of that work eventually was merged.</p><p>But in 2026, I had a Codex subscription and in a few days my token usage would reset. When it happens, I want to make sure I use my subscription as much as possible, otherwise these tokens would be wasted.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wVhP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wVhP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 424w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 848w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 1272w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wVhP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png" width="524" height="237.4375" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1024,&quot;resizeWidth&quot;:524,&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_!wVhP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 424w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 848w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 1272w, https://substackcdn.com/image/fetch/$s_!wVhP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b58e41-15a5-49d9-8428-79a62c7ac934_1024x464.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">My end goal with tokenmaxing - no token usage left before the reset day.</figcaption></figure></div><p>I use these tokens to clear the backlog in many of the open source projects I&#8217;ve been working on for the last few years.</p><p>One of these projects was the Course Management Platform and I finally reached the Tailwind issue. I had wanted to redesign the platform since the day I created it. It was functional and was already saving a lot of time but the look could definitely be improved. I thought that the day to do it finally had come.</p><p>So I dropped the issue into the Codex session and asked it to create a redesign. I don&#8217;t have any screenshots from that day, but the output was terrible. I didn&#8217;t like the one-shotted design at all. It screamed &#8220;AI-generated&#8221; at me and looked like millions of other websites on the internet.</p><h2>Using GPT Image to Generate Interface Mockups</h2><p>Around that time, <a href="https://developers.openai.com/api/docs/models/gpt-image-2">GPT Image 2</a> came out. People on X were posting AI-generated screenshots with the caption &#8220;this is not a screenshot&#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_!WXxV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WXxV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 424w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 848w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WXxV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png" width="324" height="502.0823244552058" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1280,&quot;width&quot;:826,&quot;resizeWidth&quot;:324,&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_!WXxV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 424w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 848w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!WXxV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a703feb-e299-4f24-aa93-a5e0455d00ca_826x1280.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"><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"><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"><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"><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">ChatGPT generated a screenshot of my conversation with Elon Musk.</figcaption></figure></div><p>I thought that if GPT Image is so good at generating screenshots, maybe it&#8217;s also good at generating interfaces. </p><p>I took a few screenshots of the Course Management Platform and asked ChatGPT to redesign it. With a few iterations I got a result that I liked. It wasn&#8217;t the final result, but the direction was definitely good.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vHUj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vHUj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vHUj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png" width="504" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bbdd025e-f904-424d-9625-caa58881590b_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:504,&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_!vHUj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vHUj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbdd025e-f904-424d-9625-caa58881590b_1024x1536.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"><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"><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"><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"><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">Desktop mockup for the Data Engineering Zoomcamp dashboard, with homework statuses and project attempts.</figcaption></figure></div><p>Once the desktop version was ready, I also asked to generate the mobile ones.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AOlY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AOlY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 424w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 848w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 1272w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AOlY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png" width="358" height="650.9440860215054" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1691,&quot;width&quot;:930,&quot;resizeWidth&quot;:358,&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_!AOlY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 424w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 848w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.png 1272w, https://substackcdn.com/image/fetch/$s_!AOlY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4b4540a-e089-4ce9-ac9b-180a355f325c_930x1691.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"><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"><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"><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"><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">Mobile mockup for Homework 1, including questions, submission fields, and the submit button.</figcaption></figure></div><p>Then I gave the results to Codex and asked it to build the pages in that style. The result was far from identical, but directionally it was good.</p><p>After that, I needed to iterate on the pages separately. I moved things around a bit until I got the result I personally wanted.</p><h2>Using GitHub Primer as the Design System</h2><p>The first pages were ready, and I started redesigning other screens.</p><p>I immediately saw that the style drifted from the original design mockups:</p><ul><li><p>The buttons and other UI elements were placed in unexpected places</p></li><li><p>The CSS styles for these UI elements were not consistent across the pages</p></li><li><p>Also, the gaps between elements were different. Even the width of the pages wasn&#8217;t consistent</p></li></ul><p>I needed to come up with a specification of how exactly the elements should look like, where they should be placed, the gaps between them, the colors and the styles. I needed a design reference that the agents could use when implementing the changes or adding new elements.</p><p>I started talking with Codex and told it to find a good reference that actually fits our system, so we can base all the design decisions on it.</p><p>Codex suggested GitHub Primer and I liked it a lot, so we documented the result in <a href="https://github.com/DataTalksClub/course-management-platform/blob/main/docs/design-system.md">design-system.md</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_!u13N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u13N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 424w, https://substackcdn.com/image/fetch/$s_!u13N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 848w, https://substackcdn.com/image/fetch/$s_!u13N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 1272w, https://substackcdn.com/image/fetch/$s_!u13N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u13N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png" width="1456" height="688" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:688,&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_!u13N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 424w, https://substackcdn.com/image/fetch/$s_!u13N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 848w, https://substackcdn.com/image/fetch/$s_!u13N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.png 1272w, https://substackcdn.com/image/fetch/$s_!u13N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4138018b-9c05-41ba-8f29-6fd2024b0aec_2048x968.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"><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"><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"><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"><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 href="https://primer.style/">GitHub Primer</a> &#8211; the design system behind GitHub</figcaption></figure></div><h2>The Algorithm</h2><p>Just giving the mockups and the design system wasn&#8217;t enough, so I still needed to check all the pages and see things that I didn&#8217;t like. I then asked to move them around until I liked the positions and styling.</p><p>There are still a lot of things that I want to improve, especially in the internal part of the website for the instructors. But overall I&#8217;m quite satisfied with the outcome. You can check the platform at <a href="https://courses.datatalks.club/">https://courses.datatalks.club/</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_!mAAc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mAAc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 424w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 848w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 1272w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mAAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png" width="1280" height="678" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:678,&quot;width&quot;:1280,&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_!mAAc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 424w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 848w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.png 1272w, https://substackcdn.com/image/fetch/$s_!mAAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0612e0b-878d-4d79-9060-a189ad24258f_1280x678.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"><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"><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"><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"><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 redesigned course platform homepage, with active courses and registration in the new style.</figcaption></figure></div><p></p><p>So the algorithm I arrived at was:</p><ol><li><p>Describe to ChatGPT what kind of site I want and what should be on the page</p></li><li><p>Ask it to generate a mockup image</p></li><li><p>Iterate on the image until I like the direction</p></li><li><p>Give the image to the coding agent and ask it to implement the layout</p></li><li><p>Check the results, note the things you don&#8217;t like, fix a few of them</p></li><li><p>Ask the agent to pick up a reference design system (or a combination of multiple) and create the design system document</p></li><li><p>Add new pages to your project, make sure the agents follow the design system, and when you don&#8217;t like what they create design-wise, ask them to document that in the system</p></li></ol><p>This approach also worked well for other websites that I work on, like <a href="https://aishippinglabs.com/">AI Shipping Labs</a>. </p><h2>Android Applications</h2><p>It also works well for other user-facing applications, not only web.</p><p>Right now I am working on an app that lets me manage agents from my phone. I call it <a href="https://github.com/alexeygrigorev/pocketshell">Pocket Shell</a>. The first version already works, but the interface is still rough.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9lmp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9lmp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 424w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 848w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9lmp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png" width="590" height="588.9388489208633" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1110,&quot;width&quot;:1112,&quot;resizeWidth&quot;:590,&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_!9lmp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 424w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 848w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!9lmp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880cfafe-0dd7-4463-a1a4-8e6eecd5f58b_1112x1110.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"><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"><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"><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"><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 Pocket Shell looked before: the project tree screen and the agent conversation screen.</figcaption></figure></div><p>I took these screenshots and asked ChatGPT to redesign it. It took a few iterations until I arrived at the result I liked (directionally).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qXjY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qXjY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 424w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 848w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 1272w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qXjY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png" width="950" height="590" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9659f979-9056-4122-832c-1f99c008522a_950x590.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:590,&quot;width&quot;:950,&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_!qXjY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 424w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 848w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.png 1272w, https://substackcdn.com/image/fetch/$s_!qXjY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9659f979-9056-4122-832c-1f99c008522a_950x590.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"><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"><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"><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"><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">What I arrived at with ChatGPT after a few iterations</figcaption></figure></div><p>I used the mockups to set the direction, then polished the details in code. There&#8217;s still a lot of work in the app, but I like the current screens much more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LGbu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LGbu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 424w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 848w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 1272w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LGbu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png" width="1010" height="1022" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1022,&quot;width&quot;:1010,&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_!LGbu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 424w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 848w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.png 1272w, https://substackcdn.com/image/fetch/$s_!LGbu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb41fd27b-509d-4582-a5a6-c551d3c4458d_1010x1022.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"><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"><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"><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"><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">Pocket Shell current design for the project tree as well as the prompt composer.</figcaption></figure></div><p>This workflow helps me a lot with design, even though I still don&#8217;t think I&#8217;m good at it. Of course, I still rely entirely on coding agents to do the implementation, but at least now the designs that come out of the process look less generic.</p><p></p><h2>What I&#8217;ve Been Working On Recently</h2><h3>1. LLM Zoomcamp Launch</h3><p>We launched LLM Zoomcamp this Monday and 754 people joined the launch session.</p><div id="youtube2-7TuZTVwnmhk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;7TuZTVwnmhk&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/7TuZTVwnmhk?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 year I reworked most of the content and we also added a new module about orchestration.</p><p>The course is up-to-date and if you&#8217;re looking to start with AI Engineering, you can do it now. Join us for the next 10 weeks and let&#8217;s learn together!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/DataTalksClub/llm-zoomcamp&quot;,&quot;text&quot;:&quot;Join LLM Zoomcamp&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/DataTalksClub/llm-zoomcamp"><span>Join LLM Zoomcamp</span></a></p><p>Also, at the beginning of the stream I asked people how they discovered the course and a lot of people said that AI recommended it. That&#8217;s really cool! I&#8217;m happy that ChatGPT and Claude are making the right recommendations when it comes to courses.</p><p>Also, many AI Shipping Labs community members are doing the course too, and <a href="https://aishippinglabs.com/events/groups/llm-zoomcamp-2026-office-hours">I&#8217;ll be hosting office hours there</a>.  </p><h3>2. Vector Search Workshop at AI Shipping Labs</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3BVd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3BVd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 424w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 848w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 1272w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3BVd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png" width="678" height="365.0769230769231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.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;:678,&quot;bytes&quot;:337862,&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/201442459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.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_!3BVd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 424w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 848w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.png 1272w, https://substackcdn.com/image/fetch/$s_!3BVd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46782ab-1365-4b37-9d19-3d3ff2dbd21e_2404x1294.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"><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"><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"><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"><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 week I run <a href="https://aishippinglabs.com/workshops/2026-06-09-vector-search-sqlite">another workshop at AI Shipping Labs</a>. During our first ever workshop in April I wanted to deploy an agent end-to-end: create a FastAPI backend, a frontend and deploy it to the cloud. My original plan was also to include vector search, but we didn&#8217;t have time back then. This week we finally did it. </p><p>As the service for hosting the database, we chose <a href="https://turso.tech/">Turso</a> and used <a href="https://github.com/alexeygrigorev/sqlitesearch">SQLiteSearch</a> on top of it to do vector search. You can check the code <a href="https://github.com/AI-Shipping-Labs/workshops/tree/main/2026/2026-06-09-vector-search-sqlite">here</a>.</p><p>During the workshop, we also discovered <a href="https://developers.cloudflare.com/vectorize/">CloudFlare Vectorize</a> - a fully serverless vector database. I want to learn how to use it and cover in the next workshops.</p><p></p><h2>My Talk &#8220;Managing AWS Access for Humans and AI Agents&#8221;</h2><div id="youtube2-bScTPc0RnXU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;bScTPc0RnXU&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/bScTPc0RnXU?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>On Tuesday I gave a talk at Berlin AWS Group meetup, where I shared my approach for providing AWS Access in low-trust environments like public workshops or for your AI agents.</p><p>I plan to write an article based on this talk, but you can already check the video.</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>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_!u1Br!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u1Br!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 424w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 848w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 1272w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u1Br!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png" width="1456" height="839" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:839,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:604942,&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/201442459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.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_!u1Br!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 424w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 848w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.png 1272w, https://substackcdn.com/image/fetch/$s_!u1Br!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef38076f-935d-4fb3-9616-baf15914bc22_1940x1118.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"><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"><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"><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"><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><strong><a href="https://github.com/run-llama/liteparse">LiteParse</a></strong> is a fast, open-source document parser from the LlamaIndex team that runs entirely on your machine with no cloud dependencies or proprietary LLM features. It does spatial text parsing with bounding boxes via PDFium, handles PDF, DOCX, XLSX, PPTX, and images, and offers selective OCR through bundled Tesseract or any HTTP OCR server. The Rust core ships with bindings for Python, Node.js/TypeScript, the browser (WASM), and a CLI, so you can plug it into a local document pipeline from whatever stack you use.</p></li></ul><p></p><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[How to Set Up Your Coding Agent: A Step-by-Step Guide]]></title><description><![CDATA[Start with one real task, then add skills, subagents, and extra tools when you need them]]></description><link>https://aishippingblog.com/p/how-to-set-up-your-coding-agent-a</link><guid isPermaLink="false">https://aishippingblog.com/p/how-to-set-up-your-coding-agent-a</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 05 Jun 2026 16:57:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_Re!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>People keep asking me how to get started with coding agents.</p><p>At first, this puzzled me. The answer sounded obvious: install it and start using it. Besides, there&#8217;s already plenty of material. Anthropic offers a free <a href="https://anthropic.skilljar.com/claude-code-101">Claude Code 101</a> course and comprehensive <a href="https://code.claude.com/docs/en/overview">Claude Code docs</a>, and OpenAI has a <a href="https://developers.openai.com/codex/quickstart">Codex quickstart</a>.</p><p>But that&#8217;s not what people are really asking. The real question is bigger - It&#8217;s not &#8220;how do I start with Claude Code&#8221; but multiple questions in one:</p><ul><li><p>How do I configure Claude Code with the correct model and permissions?</p></li><li><p>Which <a href="https://code.claude.com/docs/en/commands">slash commands</a> should I learn?</p></li><li><p>Do I need <a href="https://docs.claude.com/en/docs/claude-code/skills">skills</a>, <a href="https://code.claude.com/docs/en/sub-agents">subagents</a>, <a href="https://code.claude.com/docs/en/plugins">plugins</a>, and <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP servers</a>?</p></li><li><p>Should I run it locally, in Docker, or remotely?</p></li><li><p>Can I use it from my phone?</p></li><li><p>What about all the tricks people post on X every day, like the Ralph loop or <a href="https://code.claude.com/docs/en/agent-teams">teams of agents</a>?</p></li><li><p>Can someone show me exactly which buttons to press, and in which order?</p></li></ul><p>I understand where these questions come from. Open X and you see an endless stream of people sharing what they built and which tricks they used. I add to that stream too, with this newsletter.</p><p>This creates FOMO. You start thinking you need to figure out everything before you can start. But you don&#8217;t, and in this newsletter I&#8217;ll explain why.</p><p>Here&#8217;s how I&#8217;d get started with a coding agent if I were starting from scratch:</p><ol><li><p>Choose an assistant</p></li><li><p>Use it on a real task</p></li><li><p>Try automation</p></li><li><p>Document repeated work</p></li><li><p>Turn useful documents into skills</p></li><li><p>Use subagents when the context becomes a problem</p></li></ol><p>Work through these steps in order. Don&#8217;t start with the advanced setup or you risk getting even more confused.</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>Step 1: Choose an Assistant</h2><p>There are many AI coding assistants now:</p><ul><li><p><a href="https://code.claude.com/docs/en/overview">Claude Code</a></p></li><li><p><a href="https://developers.openai.com/codex/quickstart">Codex</a></p></li><li><p><a href="https://opencode.ai/">OpenCode</a></p></li><li><p><a href="https://github.com/features/copilot">GitHub Copilot</a></p></li><li><p><a href="https://antigravity.google/">Antigravity</a></p></li><li><p><a href="https://docs.cline.bot/cline-overview">Cline</a></p></li></ul><p>I used to recommend GitHub Copilot because, for $10 per month, you could get quite far, but that&#8217;s no longer how I think about it. Copilot&#8217;s billing is now token-based, which makes it expensive to use.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FTLA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FTLA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 424w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 848w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FTLA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png" width="1456" height="844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:844,&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_!FTLA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 424w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 848w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!FTLA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30bc1c1d-9f97-4ba7-a6a6-7d778f82c867_1832x1062.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"><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"><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"><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"><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 Copilot&#8217;s shift from a flat ten-dollar plan to usage-based billing</figcaption></figure></div><p>Right now, the best value for many people is ChatGPT Plus, which costs $20/month, because <a href="https://help.openai.com/en/articles/11369540-codex-in-chatgpt">Codex is included with ChatGPT plans</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_!Uht9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Uht9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 424w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 848w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 1272w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Uht9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png" width="1456" height="660" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:660,&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_!Uht9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 424w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 848w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.png 1272w, https://substackcdn.com/image/fetch/$s_!Uht9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3232cbfa-b340-4146-9afa-1ffc445bdde8_2048x929.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"><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"><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"><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"><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>Claude Code is also included with Claude Pro ($20/month) and Max ($100/month or $200/month). But the $20/month plan limits can disappear quickly, and it won&#8217;t get you far. If you use Claude Code a lot, you&#8217;d probably need to switch to the $100/month plan.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i2WG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i2WG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 424w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 848w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 1272w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i2WG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png" width="1456" height="690" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:690,&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_!i2WG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 424w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 848w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.png 1272w, https://substackcdn.com/image/fetch/$s_!i2WG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48ab7c38-66a8-4b8b-ac4b-1170799c81f8_2048x971.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"><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"><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"><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"><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 tools differ, and I can&#8217;t say one is clearly better for everyone. At the beginning, pick the one that fits your budget.</p><p>Don&#8217;t spend too much time comparing them. You&#8217;ll learn more from one finished task than from reading another thread about which agent is best.</p><h2>Step 2: Use The Assistant On One Real Task</h2><p>After you pick an agent, use it on something small enough to check end-to-end.</p><p>For example:</p><ul><li><p>Write a Python or bash script</p></li><li><p>Solve homework from a course</p></li><li><p>Build a mini-project, like the snake game we did in <a href="https://github.com/DataTalksClub/ai-dev-tools-zoomcamp">AI Dev Tools Zoomcamp</a></p></li><li><p>Fix a small problem in a project you already use</p></li></ul><p>You don&#8217;t need MCPs, skills, subagents, plugins, or custom prompts yet. At this stage, you only need to check two things: can the agent solve your problem, and how do you like to work with it?</p><h3>Decide How Much Permission To Give It</h3><p>When you start, the agent asks for permission for every action. It gets annoying fast.</p><p>You have two options:</p><ul><li><p>Build an <a href="https://code.claude.com/docs/en/permissions">allow-list of approved actions</a>: Every time the agent requests a command, you approve it and tell it not to ask for that action again. Over time, the useful commands become automatic.</p></li><li><p>Run the agent in <a href="https://code.claude.com/docs/en/permissions">skip-permissions mode</a>, also called YOLO mode: The agent can run any command executable from your terminal. You have to understand the risk. If the agent decides to delete something, it can.</p></li></ul><p>I run most of my projects in YOLO mode. Sometimes speed matters more than control. I want to hand the agent a task for the night and walk away without having to approve every single step.</p><p>For serious tasks, I use the allow-list approach. If I&#8217;m touching production infrastructure, Terraform, cloud permissions, billing, or anything where a mistake is expensive, I don&#8217;t use YOLO. Well, I <a href="https://alexeyondata.substack.com/p/how-i-dropped-our-production-database">tried once</a>, and it taught me a good lesson.</p><p>There&#8217;s no universal rule. It depends on how much control you want and how much the result matters.</p><h2>Step 3: Use Agents For Automation</h2><p>I&#8217;d been using coding agents for a long time, and they were already useful, but I realized how valuable they are only when I started using them for automation.</p><p>For example, right now, I&#8217;m preparing videos for <a href="https://github.com/DataTalksClub/llm-zoomcamp">LLM Zoomcamp</a>. I need to download videos from YouTube, cut them into separate chunks, and <a href="https://www.youtube.com/playlist?list=PL3MmuxUbc_hLZFNgSad56pDBKK8KO0XIv">upload them back</a> as individual videos. I also want each video to have time codes, so I download the subtitles and turn them into chapters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7EQo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7EQo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7EQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg" width="1280" height="828" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:828,&quot;width&quot;:1280,&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_!7EQo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7EQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2aecb99-0f21-4d78-b5b7-a34a108d9a7c_1280x828.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"><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"><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"><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"><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 delegate most of this to Claude: I only need to</p><ul><li><p>Check that the cuts are clean</p></li><li><p>Bulk-upload the videos, because the support for that in YouTube API is limited</p></li><li><p>Hit Save so each video goes from draft to published, because there&#8217;s no API for that either</p></li></ul><p>The rest is on Claude: it sees the uploaded videos, pulls their IDs, updates the title and description, adds the timecodes, and puts them into the playlist in the right order.</p><p>Creating this kind of automation is pretty simple. I just had to describe what I needed, and Claude found the video links, downloaded them, fetched the transcripts, cut the videos with <a href="https://www.ffmpeg.org/">ffmpeg</a>, and walked me through setting up <a href="https://developers.google.com/youtube/v3">YouTube API</a> access. After that, I checked the cuts and uploaded the videos.</p><p>Overall, if a service has an API or a command-line tool, an AI agent can figure out how to use it from the terminal.</p><p>Over the last year, I automated many things this way:</p><ul><li><p>Creating homework submission forms for <a href="https://courses.datatalks.club/">courses.datatalks.club</a></p></li><li><p>Creating GitHub repositories</p></li><li><p>Publishing new versions of the <a href="https://alexeyondata.substack.com/p/5-useful-utilities-i-built-with-ai">libraries I maintain</a></p></li></ul><h2>Step 4: Document Repeated Processes</h2><p>Once I automate something and know I&#8217;ll need it again, I document it.</p><p>Usually, I work with the agent until the task is done, and then I ask it to write down the process in a markdown file. The LLM Zoomcamp docs for cutting YouTube videos live <a href="https://github.com/DataTalksClub/llm-zoomcamp/tree/8c1834d114754cc0e0d65544b8589ef7d94b81cf/docs">here</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_!s0kS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s0kS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 424w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 848w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 1272w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s0kS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png" width="1456" height="831" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:831,&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_!s0kS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 424w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 848w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.png 1272w, https://substackcdn.com/image/fetch/$s_!s0kS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c2d1dc8-e755-407a-a5ef-b88b4fe1f1df_2048x1169.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"><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"><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"><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"><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>Next time, I can point the agent at the doc, and when I needed to add more videos to the LLM Zoomcamp playlist, that&#8217;s exactly what I did.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HVn8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HVn8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 424w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 848w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 1272w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HVn8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png" width="1280" height="706" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:706,&quot;width&quot;:1280,&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_!HVn8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 424w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 848w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.png 1272w, https://substackcdn.com/image/fetch/$s_!HVn8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F227166f5-79dd-4463-b080-85b5567f80a7_1280x706.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"><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"><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"><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"><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">Automating YouTube playlist management by pointing the agent at the existing docs</figcaption></figure></div><p>Without a doc, the agent starts from scratch and burns time and tokens figuring out the same thing again.</p><p>For YouTube, this may not matter much, since there&#8217;s plenty of information online and the agent already knows a lot. But for less common workflows, it doesn&#8217;t have the context.</p><p>For example, I use agents to publish homework to <a href="https://courses.datatalks.club/">courses.datatalks.club</a>. To do that, the agent needs to know:</p><ul><li><p>The production URL</p></li><li><p>Where the API key lives</p></li><li><p>What is available in the API</p></li><li><p>The exact payload shape</p></li><li><p>What to do when things go wrong</p></li></ul><p>Without a doc, I need to explain this every time.</p><p>With a doc, I can just say &#8220;use this process&#8221; and let the agent continue from there.</p><h2>Step 5: Turn Useful Documents Into Skills</h2><p>Once a document becomes part of your regular process, turn it into a skill.</p><p>A skill is a markdown file saved in a specific folder with a specific name.</p><p>For <a href="https://docs.claude.com/en/docs/claude-code/skills">Claude Code skills</a>, the folder is:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;cb2c5dfd-553b-4db3-9275-8127bad12840&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">.claude/skills/&lt;skill-name&gt;/SKILL.md</code></pre></div><p>For <a href="https://openai.com/academy/codex-plugins-and-skills/">Codex skills</a>, the folder is:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;1e6d68b8-c382-4cbc-ad68-e8c62ebabecf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">.codex/skills/&lt;skill-name&gt;/SKILL.md</code></pre></div><p>The file needs YAML frontmatter with a name and a description. That&#8217;s the only required part.</p><p>Here&#8217;s an example from my <a href="https://github.com/alexeygrigorev/course-management-agent">course-management-agent</a> repo:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;bbf876b6-bd7d-409a-9698-532bbd64d492&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">---
name: course-content
description: Manage courses, homeworks, and projects via REST API
---
# Course Content API
## Overview
This skill provides commands to manage courses, homeworks, and projects via the REST API. Supports list, create, update state/dates/description, and guarded delete.</code></pre></div><p>Once the file is in the right place, the agent picks it up automatically. With a skill in place, I can write a request as usual: &#8220;Here is the homework, create a form for it.&#8221; The agent recognizes the skill, loads it, and does the rest.</p><p>Without the skill, I need to point it at the docs every time.</p><p>You don&#8217;t have to write skills by hand. Ask the agent to document the process and turn it into a skill, and Claude Code and Codex both have built-in help for creating skills, so they can put the file in the right folder and format.</p><p>If you&#8217;re wondering whether something should become a skill, don&#8217;t overthink it. Document what you do at the end of each coding session, and if you come back to that document repeatedly, turn it into a skill.</p><div class="callout-block" data-callout="true"><p>For a more structured take, Anthropic published <a href="https://claude.com/blog/lessons-from-building-claude-code-how-we-use-skills">Lessons from building Claude Code: How we use skills</a>. OpenAI also has a <a href="https://github.com/openai/skills">Codex skills catalog</a>.</p></div><h2>Step 6: Use Subagents When Context Gets Too Large</h2><p>As you work with an agent, every question and answer accumulates in the context. Eventually, the context fills up and the quality drops.</p><p>I first noticed how useful subagents are while setting up my <a href="https://alexeyondata.substack.com/p/telegram-assistant">Telegram writing assistant</a>.</p><p>It works like this:</p><ul><li><p>I send it content: voice notes, text messages, links, videos</p></li><li><p>I run <code>/process</code> from Telegram</p></li><li><p>Claude turns the pile into articles, summaries, and saved notes</p></li></ul><p>The <code>/process</code> command is a <a href="https://github.com/alexeygrigorev/telegram-writing-assistant/blob/master/process/process.md">skill</a> that describes how to turn the input into writing material.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CSOy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CSOy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 424w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 848w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 1272w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CSOy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png" width="1456" height="854" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:854,&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_!CSOy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 424w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 848w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.png 1272w, https://substackcdn.com/image/fetch/$s_!CSOy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2cc79f7-e4b2-4adc-b0e3-d42b9b0349a3_1456x854.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"><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"><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"><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"><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><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!awH7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!awH7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 424w, https://substackcdn.com/image/fetch/$s_!awH7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 848w, https://substackcdn.com/image/fetch/$s_!awH7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 1272w, https://substackcdn.com/image/fetch/$s_!awH7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!awH7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png" width="1162" height="866" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:866,&quot;width&quot;:1162,&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_!awH7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 424w, https://substackcdn.com/image/fetch/$s_!awH7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 848w, https://substackcdn.com/image/fetch/$s_!awH7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.png 1272w, https://substackcdn.com/image/fetch/$s_!awH7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55a39ed7-ea66-40e3-9da8-7a30ba72f68c_1162x866.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"><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"><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"><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"><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>Now imagine I send this:</p><ul><li><p>Ten voice notes that should become two or three articles</p></li><li><p>A tweet I liked and want to save</p></li><li><p>A three-hour YouTube video I want to summarize</p></li></ul><p>If I run <code>/process</code> and the main agent handles everything in the same context, the long YouTube transcript pollutes the rest of the work: the article draft gets worse, the summary isn&#8217;t great either, and after that, there&#8217;s no clean context to continue from.</p><p>One way out is to process the simple items first and handle the YouTube video in a separate session later.</p><p>But there&#8217;s an alternative: the main agent starts a subagent with specific instructions: summarize this transcript. The subagent runs in its own context, saves the result, and reports back. The main agent only sees the task, the status, and the final summary. The huge transcript never enters the main context.</p><p>That&#8217;s the main reason I <a href="https://alexeyondata.substack.com/p/i-turned-my-telegram-bot-into-a-multi">use subagents</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_!rasO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rasO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 424w, https://substackcdn.com/image/fetch/$s_!rasO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 848w, https://substackcdn.com/image/fetch/$s_!rasO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 1272w, https://substackcdn.com/image/fetch/$s_!rasO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rasO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png" width="1456" height="828" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:828,&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_!rasO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 424w, https://substackcdn.com/image/fetch/$s_!rasO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 848w, https://substackcdn.com/image/fetch/$s_!rasO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.png 1272w, https://substackcdn.com/image/fetch/$s_!rasO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F158c9d5e-9dc5-4d00-b174-5fd5927d4acd_1456x828.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"><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"><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"><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"><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 the task is small and the context is clean, I keep it in the main session. If the task is large, noisy, or research-heavy, I send it to a subagent.</p><p>Each subagent works on a specific task:</p><ul><li><p>Summarize this long transcript and save the notes</p></li><li><p>Inspect this large repository and report the architecture</p></li><li><p>Review these logs and extract the failure pattern</p></li><li><p>Search these files and return the exact places that need changes</p></li></ul><h2>Other Tips</h2><h3>Keep Project Context Files</h3><p>In my own projects, I usually create a <a href="https://code.claude.com/docs/en/memory">CLAUDE.md</a> and an <a href="https://developers.openai.com/codex/guides/agents-md">AGENTS.md</a>.</p><p>Claude Code reads <code>CLAUDE.md</code>. Codex and <a href="https://opencode.ai/docs/rules/">OpenCode</a> read <code>AGENTS.md</code>.</p><p>These files explain the project to the agent:</p><ul><li><p>What the project does</p></li><li><p>How to run tests</p></li><li><p>Which commands are safe</p></li><li><p>Where important files live</p></li><li><p>What conventions to follow</p></li><li><p>What not to touch</p></li></ul><p>The agent reads this file when it starts. That saves me from explaining the project from scratch each time and spares the agent from crawling through the whole repository to figure out how things work.</p><p>Because I reset sessions often, these files matter a lot.</p><h3>Reset The Session Often</h3><p>The subcommand I use most often is the one that resets the session. It&#8217;s called reset, new or clear, depending on the agent.</p><p>Every new task starts with a clean context. The agent reads the most important instructions from <a href="http://agents.md">AGENTS.md</a> and starts working on the task</p><h3>Use Goals For Long-Running Work</h3><p>Another command I use actively right now is <a href="https://code.claude.com/docs/en/goal">goal</a>.</p><p>You give the agent a completion condition in plain language, for example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;f472664e-f513-468f-84bd-e51c60f42c9a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">/goal work through the backlog</code></pre></div><p>Then the agent keeps starting new turns until the condition is met.</p><p>It&#8217;s the productized version of <a href="https://alexeyondata.substack.com/p/my-experiments-with-claude-code">the Ralph loop</a>: a loop that keeps feeding work to a the agent, until a certain condition is met.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3wyh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3wyh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3wyh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg" width="1280" height="781" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:781,&quot;width&quot;:1280,&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_!3wyh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3wyh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cedad65-a29c-4f42-8062-9044450457c0_1280x781.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"><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"><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"><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"><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">Report from Codex about the achieved goal</figcaption></figure></div><p>Codex <a href="https://developers.openai.com/codex/use-cases/follow-goals">shipped it</a> first. Claude Code <a href="https://developers.openai.com/codex/use-cases/follow-goals">added a near-identical version</a> shortly after, with a separate small evaluator model that checks whether the condition is satisfied after each turn and, if not, sends guidance and starts another turn. It pairs well with skip-permissions mode, so each turn runs without approval prompts.</p><p>Beyond <code>reset</code> and <code>goal</code>, I don&#8217;t use many slash commands.</p><h3>My Setup Lives In Dotfiles</h3><p>To understand what others are doing, I recommend that you first take all the steps that I described in the article. Complex setups can be confusing, especially when you&#8217;re just starting to work with agents.</p><p>Here, I&#8217;d like to share my own setup, which is available in a public repository: <a href="https://github.com/alexeygrigorev/.claude">github.com/alexeygrigorev/.claude</a>. Despite the name, it bootstraps and configures all three agents I use from one shared clone. Every machine and every assistant gets the same setup.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yqN-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yqN-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 424w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 848w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 1272w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yqN-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png" width="1456" height="944" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.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;: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_!yqN-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 424w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 848w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.png 1272w, https://substackcdn.com/image/fetch/$s_!yqN-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7e82d95-76d8-401d-a7e2-d7804226badd_1844x1196.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"><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"><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"><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"><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>Here&#8217;s what&#8217;s inside this repo and the role of each file:</p><ul><li><p>An installer script clones the repo</p></li><li><p>A configure script wires everything into the right home-directory locations</p></li><li><p>The configure step takes a target: <code>claude</code>, <code>codex</code>, <code>opencode</code>, or <code>all</code></p></li><li><p>A <code>config/</code> folder holds per-assistant settings</p></li><li><p>A <code>skills/</code> folder holds the shared skills</p></li><li><p>A shared <code>.bashrc</code> defines the aliases I use daily</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_!yrXY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yrXY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 424w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 848w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 1272w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yrXY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png" width="1456" height="1016" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1016,&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_!yrXY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 424w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 848w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.png 1272w, https://substackcdn.com/image/fetch/$s_!yrXY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3d6529f-e735-4e01-8323-4a560e0945f8_1766x1232.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"><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"><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"><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"><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 skills are symlinked into each assistant, so Claude Code, Codex, and OpenCode can share them.</p><p>Some examples from that repo:</p><ul><li><p><code>create-github-repo</code></p></li><li><p><code>fetch-youtube</code></p></li><li><p><code>fetch-loom</code></p></li><li><p><code>fetch-google-recorder</code></p></li><li><p><code>init-library</code></p></li><li><p><code>jina-reader</code></p></li><li><p><code>openai-transcribe</code></p></li><li><p><code>regular-ping</code></p></li><li><p><code>release</code></p></li><li><p><code>setup-pypi-ci</code></p></li><li><p><code>stylint</code></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_!t_Re!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t_Re!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 424w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 848w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t_Re!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png" width="1456" height="717" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:717,&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_!t_Re!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 424w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 848w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!t_Re!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F861c859f-87a4-4cd1-81a7-907dc230b05d_2048x1008.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"><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"><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"><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"><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 <code>.bashrc</code> also defines short aliases. I use <code>c</code> for Claude, <code>cc</code> for continue-session, <code>csp</code> for Claude with skip permissions, <code>cy</code> for Codex in bypass mode, and <code>oc</code> for OpenCode.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZUhb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZUhb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 424w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 848w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 1272w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZUhb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png" width="1456" height="799" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.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_!ZUhb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 424w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 848w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.png 1272w, https://substackcdn.com/image/fetch/$s_!ZUhb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f04be44-9443-4801-9fda-ecc450dd4fde_1790x982.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"><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"><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"><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"><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 repo also has guardrails. Claude settings register a <code>PreToolUse</code> hook that blocks dangerous commands unless I confirm them. For example, it blocks rm <code>-rf /</code>, force pushes, dropping a database, and <code>terraform apply</code>.</p><p>This setup isn&#8217;t required on day one. I built it after I&#8217;d faced the same tasks enough times to get annoyed. Don&#8217;t overcomplicate things, install any agent and just start experimenting with it for solving your tasks. Along the way, you&#8217;ll figure out the rest and which agent capabilities you really need.</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>What I&#8217;ve Been Working On This Week</h2><h3>Preparing For The Upcoming Workshop On Running Durable Agents In Production</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kSZ0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSZ0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSZ0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg" width="306" height="306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:800,&quot;resizeWidth&quot;:306,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cover Image for Running Durable Agents 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="Cover Image for Running Durable Agents in Production" title="Cover Image for Running Durable Agents in Production" srcset="https://substackcdn.com/image/fetch/$s_!kSZ0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!kSZ0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd555e3d6-9cb9-4c5e-ad69-15a606186779_800x800.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"><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"><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"><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"><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 preparing for the upcoming workshop on <a href="https://luma.com/wz20rm8n">Running Durable Agents in Production</a>.</p><p>Agent demos often work well until something interrupts them: the process crashes, a deploy happens, a tool call fails, or an approval never comes back. In production, this means the workflow needs a way to recover, continue, and show what happened.</p><p>That&#8217;s what Nicholas Lotz will cover in the workshop: how to save state outside the agent process, resume after failures, retry tool calls, pause safely for approvals, inspect execution traces, and deploy multi-agent apps to real infrastructure.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://luma.com/wz20rm8n&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://luma.com/wz20rm8n"><span>Register here</span></a></p><h3>Monitoring LLM Applications Workshop</h3><div id="youtube2-ImY5-Q97sRw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;ImY5-Q97sRw&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/ImY5-Q97sRw?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>I also worked on a workshop about <a href="https://www.youtube.com/live/ImY5-Q97sRw?si=eaCKXqMai57aIp9E">monitoring LLM applications</a>.</p><p>In the workshop, we build a Streamlit chat app, store conversations in PostgreSQL, capture LLM call metrics, add user feedback, use an LLM judge, and put everything on dashboards. We also go through Grafana, synthetic data, Docker Compose, and a few next steps, like OpenTelemetry and alerting.</p><h3>Freestyle Workshop At AI Shipping Labs</h3><p>For AI Shipping Labs, I did a freestyle coding workshop on hosting open-source LLMs with vLLM on rented RunPod GPUs.</p><p>We compared Cloud Run and Lambda pricing, picked GPUs, calculated VRAM for DeepSeek models, fixed out-of-memory errors with quantization, set up SSH, used uv, kept things alive with tmux, protected endpoints with API keys, and packaged everything into a Docker image.</p><div class="callout-block" data-callout="true"><p>If you want the notes from this workshop, <a href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_content=2026_05_22">join AI Shipping Labs</a>. The Basic plan includes the notes, and the Main plan also includes the recording.</p></div><h3>LLM Zoomcamp 2026 Pre-Course Live Q&amp;A</h3><div id="youtube2-RspWoRtittU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;RspWoRtittU&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/RspWoRtittU?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>I also hosted the <a href="https://www.youtube.com/live/RspWoRtittU?si=8VU0Vejo0OD0minm">LLM Zoomcamp 2026 pre-course Q&amp;A</a>.</p><p>The live cohort starts on June 8, so this was a chance to answer questions before the course begins. We talked about what we&#8217;ll build, who the course is for, prerequisites, homework, the leaderboard, certificates, capstone projects, and how to get the most out of the DataTalks.Club community.</p><p><a href="https://www.youtube.com/redirect?event=video_description&amp;redir_token=QUFFLUhqbXdya1hDbEFtUzNWaThHcVpPZ3llSjZEUnpSd3xBQ3Jtc0tsMVhuXzU3dFZnLTBRZ0xGMkZTbjZFUkxyM3h4cjBkSWdEQjBrd3BaNHEyX1FmOGUtWkRTNmw5TUo1S0p3UDV0bU1ScUQxcUx4X3RSbk8wN0NxbEhvT29Hc3N5eW40Z3pLZVM0aktGQVRmVl9TZURaMA&amp;q=https%3A%2F%2Fgithub.com%2FDataTalksClub%2Fllm-zoomcamp&amp;v=RspWoRtittU">LLM Zoomcamp</a> is free and open-source. Basic Python and command-line experience are enough to get started.</p><h2>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_!2301!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2301!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 424w, https://substackcdn.com/image/fetch/$s_!2301!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 848w, https://substackcdn.com/image/fetch/$s_!2301!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 1272w, https://substackcdn.com/image/fetch/$s_!2301!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2301!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png" width="1456" height="844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:844,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!2301!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 424w, https://substackcdn.com/image/fetch/$s_!2301!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 848w, https://substackcdn.com/image/fetch/$s_!2301!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.png 1272w, https://substackcdn.com/image/fetch/$s_!2301!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18f7dd3f-794b-4d4d-968c-0d2fee41bc59_2048x1187.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"><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"><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"><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"><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 href="https://github.com/dwzhu-pku/PaperBanana">PaperBanana</a> is a reference-driven multi-agent framework for automated academic illustration generation.</figcaption></figure></div><ul><li><p><strong><a href="https://github.com/dwzhu-pku/PaperBanana">PaperBanana</a></strong>: an agentic framework that automates the creation of publication-ready methodology diagrams and statistical plots directly from paper text, references, or even rough sketches. By orchestrating specialized agents for retrieval, planning, rendering, and self-critique, it reduces the manual bottleneck of academic illustration while maintaining scientific accuracy and visual consistency. It is built specifically for researchers who want high-quality figures without spending hours in drawing tools.</p></li><li><p><strong><a href="https://github.com/virattt/dexter">Dexter</a></strong>: an autonomous financial research agent that thinks, plans, and learns as it works. It performs analysis using task planning, self-reflection, and real-time market data. Think Claude Code, but built specifically for financial research. It features intelligent task planning, autonomous execution, self-validation, real-time access to financial data, and safety features such as loop detection and step limits.</p></li></ul><h2>Interesting Resources</h2><ul><li><p><strong><a href="https://github.com/timothywarner-org/claude-code">Claude Code and Large-Context Reasoning</a></strong>: materials from a hands-on O&#8217;Reilly Live Learning course by Tim Warner that teaches how to build production-ready AI-assisted development workflows with Claude Code. It covers large-context reasoning, MCP-based persistent memory, agents, and custom skills, with practical examples for code review, automation, and CI/CD.</p></li><li><p><strong><a href="https://github.com/avifenesh/awesome-slash">awesome-slash</a></strong>: a curated GitHub list of tools, patterns, and projects built around slash-command interfaces. It&#8217;s a practical reference for anyone designing command-driven workflows, bots, or developer tools that rely on concise, action-oriented commands instead of complex UIs.</p></li></ul><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[Minsearch: The Small Search Library Behind My RAG Workshops and Courses]]></title><description><![CDATA[How a small in-process Python library replaced Elasticsearch in my RAG notebooks that I use for teaching.]]></description><link>https://aishippingblog.com/p/minsearch-the-small-search-library</link><guid isPermaLink="false">https://aishippingblog.com/p/minsearch-the-small-search-library</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 29 May 2026 15:34:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FgN7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Two years ago, I was preparing the first run of <a href="https://github.com/DataTalksClub/llm-zoomcamp/tree/main">LLM Zoomcamp</a>, my free course on building LLM applications. That first run focused mostly on RAG. I was also running workshops on the same topics.</p><p>Search, or retrieval, is one of the most important parts of RAG. I needed a way to teach it without asking participants to install Docker or Elasticsearch.</p><p>So I built <a href="https://github.com/alexeygrigorev/minsearch">minsearch</a>: a small in-process Python search library. It started as the smallest thing I needed to teach retrieval in a notebook, then grew as the course examples changed.</p><p>In this post, I will share:</p><ul><li><p>Why was Elasticsearch too much for this setup</p></li><li><p>How the first version worked</p></li><li><p>How it became a PyPI package</p></li><li><p>Why I added an appendable index and vector search</p></li><li><p>How I used Claude to make it faster</p></li><li><p>When minsearch is the right tool</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_!FgN7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FgN7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 424w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 848w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FgN7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png" width="1456" height="891" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:891,&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;: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_!FgN7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 424w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 848w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!FgN7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327e7b76-3c70-4765-97a6-144b8d903ff2_1764x1080.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"><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"><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"><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"><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 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>Why Elasticsearch Was Too Much</h2><p>In the first run of LLM Zoomcamp, I needed to show participants how to index a small dataset, submit a query, and retrieve relevant documents. Usually, it was a few thousand documents, sometimes fewer. Most of the examples were run in notebooks, sometimes on Google Colab, using open-source LLMs on a GPU. And working with notebooks was my main motivation for creating minsearch as a lightweight alternative to more complex search engines.</p><p>For a normal production system, I would probably reach for <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a>. It is powerful, and I knew it well. But for a workshop notebook, it was too much. It requires a server, Docker, configuration, and operational details that weren&#8217;t the point of the lesson.</p><p>In the course or workshop setup, everything should run within a single notebook. I looked for a small Python library that could do a good-enough lexical search within the same Python process as the notebook, but I didn&#8217;t find anything that fit.</p><p>At that time, I had been doing text processing and search for quite some time, so building a small in-process search library myself wasn&#8217;t hard. Even back then, when coding agents weren&#8217;t as good as they are now, I could describe what I wanted to a chat assistant, get code back, and ask for a few fixes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kyt2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kyt2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 424w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 848w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 1272w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kyt2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png" width="1456" height="643" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:643,&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_!kyt2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 424w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 848w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.png 1272w, https://substackcdn.com/image/fetch/$s_!kyt2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0604a422-a8d0-4562-8f65-05bf3a2be890_2048x904.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"><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"><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"><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"><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 module from the first run of LLM Zoomcamp, built around minsearch.</figcaption></figure></div><h2>The First Version</h2><p>The <a href="https://github.com/alexeygrigorev/minsearch/tree/62ddbe9bc4adbc38cfd14114a2128fdf3b8e0110">first implementation</a> was a single Python file.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_8HB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_8HB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 424w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 848w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_8HB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png" width="1456" height="814" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:814,&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_!_8HB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 424w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 848w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!_8HB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fc70f54-aa46-4a85-9206-80192452418e_1968x1100.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"><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"><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"><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"><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 had one class, <code>Index</code>, and the search was just a bag-of-words with TF-IDF.</p><p>It worked like this:</p><ol><li><p>Fit a TF-IDF vectorizer for each text field</p></li><li><p>Transform the query with the same vectorizers</p></li><li><p>Multiply the matrices to get document scores</p></li><li><p>Add the scores from all text fields</p></li><li><p>Sort the documents by score</p></li></ol><p>In my teaching, I used examples with FAQs from my free courses, the Zoomcamps. A typical document had a question, an answer, a section, a course name, and some metadata.</p><p>This already required a little more than a plain text search. Matches in the question field should count more than matches in the answer field. Sometimes I wanted results only from one course. So the first version also had field boosting and keyword filtering.</p><p>That made minsearch useful for teaching. The implementation was small enough that learners could understand it, yet it still included the pieces I needed for real-world course examples: text search, filters, and boosts.</p><p>The basic usage looked like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;0d5885e3-c012-48a4-8e35-3b8e5322ea21&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">from minsearch import Index
index = Index(
    text_fields=[&#8221;question&#8221;, &#8220;answer&#8221;],
    keyword_fields=[&#8221;course&#8221;]
)
index.fit(docs)
results = index.search(&#8221;can I join the course?&#8221;)</code></pre></div><p>I also shared how I built it in the workshop titled &#8220;<a href="https://github.com/alexeygrigorev/build-your-own-search-engine">Build Your Own Search Engine</a>.&#8221; The first version of that workshop came out around two years ago, originally as a DataTalks.Club talk. But I later updated it to include the newer library versions and published it as a <a href="https://aishippinglabs.com/workshops/2026-05-14-build-your-own-search-engine">structured tutorial</a> in the <a href="https://aishippinglabs.com/workshops/">AI Shipping Labs workshop library</a>.</p><p>If you want to understand how minsearch works internally, that workshop is the best place to look.</p><h2>From a File to a Package</h2><p>At first, people downloaded the single Python file, and that worked until I needed to ship changes.</p><p>Every time I fixed something or added a feature, course participants had to download the file again. In LLM Zoomcamp, we used to do that with <code>wget</code>. It was fine for one notebook, but not for a library I kept changing.</p><p>So I packaged it properly, <a href="https://pypi.org/project/minsearch/">published it on PyPI</a>, and now people can install it with <code>uv</code> or <code>pip</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5f393763-1781-4041-8d98-f754a3dff275&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">uv add minsearch</code></pre></div><p>The first published version was <a href="https://pypi.org/project/minsearch/0.0.1/">0.0.1</a>. At the time of writing, the current version is <code>0.1.0</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_!UGnw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UGnw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 424w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 848w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 1272w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UGnw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png" width="1456" height="854" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:854,&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_!UGnw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 424w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 848w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.png 1272w, https://substackcdn.com/image/fetch/$s_!UGnw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43434ae6-4904-4a30-baa6-eb67c8584deb_2048x1201.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"><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"><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"><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"><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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/minsearch-the-small-search-library?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/p/minsearch-the-small-search-library?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><h2>Where I Use It</h2><p>I now use minsearch across my courses and workshops:</p><ul><li><p><a href="https://github.com/DataTalksClub/llm-zoomcamp">LLM Zoomcamp</a></p></li><li><p><a href="https://aishippinglabs.com/courses/aihero">AI Hero</a></p></li><li><p><a href="https://maven.com/alexey-grigorev/from-rag-to-agents">AI Engineering Buildcamp</a></p></li></ul><p>I also use it outside teaching, in personal and DataTalks.Club projects. One example is the <a href="https://github.com/DataTalksClub/faq">DataTalks.Club FAQ system</a>, where the automation reads GitHub issues and creates FAQ entries. Before adding a new question, it uses minsearch to check whether a similar question already exists.</p><p>I told you about it 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><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;c0e4eb52-2808-4e3a-94ec-5555436182a2&quot;,&quot;caption&quot;:&quot;At DataTalks.Club, we run free courses like ML Zoomcamp, Data Engineering Zoomcamp, MLOps Zoomcamp, and LLM Zoomcamp. Since 2021, we&#8217;ve been launching new cohorts every year, and with each new cohort, the same questions kept coming up.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;From Google Docs to an Automated FAQ System for DataTalks.Club Courses&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-04-24T15:55:41.542Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!RDBF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd430ca-c2e9-4185-8689-a8784deb3f96_2048x1394.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/from-google-docs-to-an-automated&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:195344115,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&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><p>The automation loads the FAQ, builds the index, searches it, and continues in one Python process.</p><p>That is still the main reason I use minsearch, but the library had to grow once the course examples changed.</p><h2>Implementing Inverted Index and Vector Search</h2><p>The appendable index came later, when I started working on the second run of LLM Zoomcamp and added the module covering agents.</p><h3>Implementing Inverted Index</h3><p>I wanted to show that agents can do more than search existing documents, like adding data back to the index and modifying it. The original index didn&#8217;t support that well. It was built for the simple case: create the index, search it, and throw it away when the notebook ends.</p><p>To allow the agent to modify the index, I implemented a new index type: <code>AppendableIndex</code>. It&#8217;s an inverted index that keeps the same <code>fit</code> and <code>search</code> methods, but it also lets you append documents one at a time.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J7b_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J7b_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 424w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 848w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 1272w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J7b_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png" width="1456" height="647" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.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;: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_!J7b_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 424w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 848w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.png 1272w, https://substackcdn.com/image/fetch/$s_!J7b_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd9be2f0-3ed7-4326-b63f-0c99f066c147_2038x906.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"><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"><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"><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"><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>Adding Vector Search</h3><p>Vector search came later for a similar reason. I had already taught it in Build Your Own Search Engine, but it wasn&#8217;t part of the library at first. Eventually, I added 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_!31PQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!31PQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 424w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 848w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 1272w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!31PQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png" width="1456" height="721" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:721,&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_!31PQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 424w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 848w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.png 1272w, https://substackcdn.com/image/fetch/$s_!31PQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e88c4a0-9bd3-4538-9d6d-a8b852bbc0b9_2002x992.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"><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"><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"><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"><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><code>VectorSearch</code> works on pre-computed embeddings and ranks results by cosine similarity. It isn&#8217;t trying to replace a full vector database. It is a simple tool for local examples, and my primary use case is to explain search concepts during my courses and workshops.</p><h3>Three Index Types and Highlighting</h3><p>Today the library has three main index types:</p><ul><li><p><code>Index</code>: The basic TF-IDF index using scikit-learn</p></li><li><p><code>AppendableIndex</code>: An inverted index implementation that lets you add documents later</p></li><li><p><code>VectorSearch</code>: Cosine similarity search over pre-computed vectors</p></li></ul><p>The same filter model works across all three. You can filter by exact keyword matches, numeric ranges, and date ranges. Keyword fields are optional now, because not every search example needs filtering.</p><p>There is also highlighting now. It extracts snippets from search results and marks where the query terms matched. The motivation was that we started to have more and more agents, and I realized that for agents, it is better to mirror how humans see.</p><p>The way humans search is that we look at the snippet, for example, in a Google search, and based on what we see, we decide whether we want to check an article. For agents, I think it works better if they can first see highlighted snippets; then, based on those snippets, they can decide whether to check the entire page for details.</p><p>I don&#8217;t see minsearch as a big infrastructure project. It grew because the examples kept needing more practical features.</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>Making minsearch Faster with Claude Code</h2><p>I used the appendable index more and eventually noticed a problem: it was much slower than the simple index.</p><p>At first, I just ignored it. The appendable index was doing more work, and the datasets were small. But at some point, it became too slow even for my use, so I decided to benchmark it.</p><p>The first benchmark showed that the appendable index was about 14 times slower to index and 27 times slower to search.</p><p>I asked Claude to look at it, and it found out the reason for this inefficiency. The appendable index recomputed tokens and scores during every search, while the simple index relied on scikit-learn&#8217;s optimized batch operations.</p><p>This was one of the first times I used an AI assistant to benchmark and optimize something like this.</p><p>I gave Claude a clear loop:</p><ol><li><p>Benchmark against Simple Wikipedia and save a baseline</p></li><li><p>Make changes to the code</p></li><li><p>Check that results still match the baseline</p></li><li><p>Compare the speed</p></li></ol><p>Then I let it run and checked in about once an hour while I worked on course materials. After a few rounds, search in the optimized appendable index was 20 to 76 times faster than the scikit-learn-based index. The gap grew on larger datasets.</p><p>The full benchmark writeup is here: <a href="https://github.com/alexeygrigorev/minsearch/blob/main/benchmark/BENCHMARK_WRITEUP.md">benchmark/BENCHMARK_WRITEUP.md</a>.</p><h2>When Minsearch Is the Right Tool</h2><p>Minsearch is a good fit when:</p><ul><li><p>You have a small or medium dataset</p></li><li><p>You need to search in a notebook, course, prototype, or small automation</p></li><li><p>Everything can live in one Python process</p></li><li><p>You are indexing up to a few thousand documents</p></li></ul><p>The sweet spot is up to 10,000 documents. At that size, indexing is fast, search is convenient, and you get a useful retrieval layer without setting up extra infrastructure.</p><p>It works best when there is enough plain text to search over. That is the case for course FAQs, workshop datasets, documentation pages, and small internal collections.</p><p>Beyond that, minsearch is no longer the right tool.</p><p>If you have a larger local dataset but still want something lightweight, use <a href="https://github.com/alexeygrigorev/sqlitesearch">SQLiteSearch</a> instead. I built it for exactly that case, and wrote about it in <a href="https://alexeyondata.substack.com/p/how-i-built-sqlitesearch-a-lightweight">How I Built SQLiteSearch</a>.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;44892f5d-c518-4505-99ee-5935b5ea11ad&quot;,&quot;caption&quot;:&quot;Recently, I was looking for a compact Python search library that provides reliable search functionality without requiring a full-scale infrastructure.&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 I Built SQLiteSearch: A Lightweight Python Library for Local Text and Vector Search&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-02-20T06:01:49.228Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!C2Wc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6555174-8977-46f7-97c1-7485775ad2cc_1600x864.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/how-i-built-sqlitesearch-a-lightweight&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:188497219,&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>For bigger systems, use a real search engine or vector database.</p><h2>What I&#8217;ve Been Working On Recently</h2><h3>1. Building pocketshell and testing agent teams</h3><p><code>pocketshell</code>, the Android app that packages my phone-based development setup, reached a point where I can actually use it while continuing to build it.</p><p>It is the next step after the workflow I described in <a href="https://alexeyondata.substack.com/p/the-system-i-built-to-ship-code-from">Working from a Phone</a>. Last week, some parts were still being set up. Now the goal is to put everything into one box, so I can do the development work I need from my phone.</p><p>I also ran an experiment with it: coordinating three coding agents, Codex, Claude Code, and OpenCode, as three team managers working on the same repo. The agents had to find each other, divide the work, and collaborate.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ee24751b-dfc3-472a-a310-e97ff069d7bb&quot;,&quot;caption&quot;:&quot;Lately, I&#8217;ve been to three conferences in a row: Darmstadt, Amsterdam, and Porto. The work doesn&#8217;t slow down for any of that, so I have to keep things moving.&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;The System I Built to Ship Code From a Phone&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-05-22T13:51:36.765Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!-mWd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/the-system-i-built-to-ship-code-from&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:198835715,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:7,&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><h3>2. Scraping more AI engineering jobs</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!r05X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!r05X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 424w, https://substackcdn.com/image/fetch/$s_!r05X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 848w, https://substackcdn.com/image/fetch/$s_!r05X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!r05X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!r05X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg" width="1080" height="726" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:726,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Job collection completion log for 2026-05-29: 2,751 scraped, 919 new deduped, 919 HTML downloaded, 919 raw and structured YAML, 0 missing structured IDs&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="Job collection completion log for 2026-05-29: 2,751 scraped, 919 new deduped, 919 HTML downloaded, 919 raw and structured YAML, 0 missing structured IDs" title="Job collection completion log for 2026-05-29: 2,751 scraped, 919 new deduped, 919 HTML downloaded, 919 raw and structured YAML, 0 missing structured IDs" srcset="https://substackcdn.com/image/fetch/$s_!r05X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 424w, https://substackcdn.com/image/fetch/$s_!r05X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 848w, https://substackcdn.com/image/fetch/$s_!r05X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!r05X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0a1c88-a961-4bc2-8aef-93be96b83ce5_1080x726.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"><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"><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"><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"><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 collection completion log for 2026-05-29: 2,751 scraped, 919 new deduped, 919 HTML downloaded, 919 raw and structured YAML, 0 missing structured IDs</figcaption></figure></div><p>I ran another scrape for the <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineering Field Guide</a>.</p><p>The goal is to keep doing this throughout the year, then analyze how the market changes over time. I want to see which requirements appear more often, which tools become more common, and how AI engineering roles evolve.</p><p>This run scraped 2,751 listings and added 919 new unique jobs after deduplication. For all 919 new jobs, the pipeline downloaded the HTML and generated both raw and structured YAML.</p><h3>3. Building AI Shipping Labs onboarding</h3><p>I started building onboarding directly into the <a href="https://aishippinglabs.com/">AI Shipping Labs</a> platform.</p><p>So far, we have manually onboarded around 40, maybe even 50, people. We went through their goals, background, current skill level, and plans by hand.</p><p>By now, we have enough material to start making this process easier. Not fully automated, but more structured.</p><h3>4. Running the O&#8217;Reilly guardrails workshop</h3><p>I ran the O&#8217;Reilly workshop this week: <a href="https://aishippinglabs.com/workshops/2026-05-26-agent-with-guardrails">Building an Agent with Guardrails</a>.</p><p>The workshop focused on how to make an agent less fragile: where to put checks, how to constrain behavior, and how to think about guardrails as part of the system rather than as something added at the end.</p><h3>5. Running LLM Zoomcamp workshops</h3><p>I also ran two LLM Zoomcamp workshops this week.</p><p>The first one was about <a href="https://www.youtube.com/watch?v=RAqLWJsLZb4&amp;pp=0gcJCR4LAYcqIYzv">agents</a>. The second one was about <a href="https://www.youtube.com/watch?v=WUGtDveIe7A">evaluations</a>. Both are part of the work to update the LLM Zoomcamp material with the topics I now use more often in practice.</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>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;: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/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="" 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"><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"><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"><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"><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> 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.</p></li><li><p><a href="https://github.com/garrytan/gstack">gstack</a> is a collection of opinionated Claude Code slash commands that transform a single AI assistant into a team of specialists, such as a CEO, an engineering manager, a release engineer, and a QA engineer. Developed by Y Combinator president Garry Tan, it offers commands like <code>/plan-ceo-review</code> for product thinking, <code>/review</code> for thorough code review, /ship for one-command PR creation, and <code>/browse</code> and <code>/qa</code> for automated browser-based testing with screenshots. It serves as a helpful reference for structuring Claude Code custom commands for multi-role development workflows.</p></li></ul><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[The System I Built to Ship Code From a Phone]]></title><description><![CDATA[A remote server, tmux, voice-driven Claude Code, and a workflow designed around the in-between time]]></description><link>https://aishippingblog.com/p/the-system-i-built-to-ship-code-from</link><guid isPermaLink="false">https://aishippingblog.com/p/the-system-i-built-to-ship-code-from</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 22 May 2026 13:51:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-mWd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lately, I&#8217;ve been to three conferences in a row: Darmstadt, Amsterdam, and Porto. The work doesn&#8217;t slow down for any of that, so I have to keep things moving.</p><p>In addition to the conferences, I&#8217;ve been traveling a lot with my child. We went away over the Easter holidays. May 1st was a long weekend in Germany. I&#8217;m dictating this just after we got back from another long weekend, 14-16 May, in Stuttgart and the Schwarzwald. And this Sunday, I&#8217;m traveling to Vienna again for <a href="https://www.viper-room.at/events/live-vienna-slam-fest-2026">a concert</a> (drop me a line if you want to meet for coffee!).</p><p>That&#8217;s a lot of traveling, and I want to use all this time to do something useful.</p><p>My day-to-day schedule is also uneven on its own:</p><ul><li><p>I take my son to school in the morning</p></li><li><p>I go to the gym</p></li><li><p>I have lunch meetings</p></li><li><p>I pick the son up at 4 PM</p></li></ul><p>Between all those slots, I have a lot of commute time. The rest periods between sets at the gym add another minute or two of free time per set.</p><p>I want to use all of that in-between time. I have too many projects running. The AI Shipping Labs site alone takes a lot of work. I want them to progress.</p><p>This article is about how I work on my phone in that spare time: on trains, buses, while commuting, between school runs, gym rest periods and even on planes.</p><p>In this post, I&#8217;ll share:</p><ul><li><p>Why I moved to a permanent remote server</p></li><li><p>The small tools that make terminal work possible from a phone</p></li><li><p>How voice became my main interface to coding agents</p></li><li><p>Tricks for handling visual tasks without access to a laptop</p></li><li><p>How this article itself was created while moving around</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_!-mWd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-mWd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 424w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 848w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 1272w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-mWd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png" width="516" height="615.058568329718" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1099,&quot;width&quot;:922,&quot;resizeWidth&quot;:516,&quot;bytes&quot;:825353,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-mWd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 424w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 848w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.png 1272w, https://substackcdn.com/image/fetch/$s_!-mWd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b63752d-a86f-4fce-81e5-23f09665232e_922x1099.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"><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"><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"><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"><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 Claude Code session on the phone, mid-task</figcaption></figure></div><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>The remote setup</h2><h3>From GitHub Actions to a dedicated server</h3><p>Working from a phone isn&#8217;t new for me. I&#8217;ve been practicing it for a long time. My whole DataTalks.Club routine was built around it so I could keep things moving from anywhere.</p><p>When GitHub Copilot appeared, I could do even more. I already wrote how I used it in <a href="https://alexeyondata.substack.com/p/shipping-features-from-a-tram-stop">Shipping Features from my Smartphone with GitHub Copilot</a>. Under the hood, it relied on GitHub Actions to do the work. It was a remote environment, but not one I owned. When the task was finished, this environment was gone, so I couldn&#8217;t really customize it for myself.</p><p>My approach changed in the last few months. I now rent a dedicated Linux server available 24/7, and Claude Code, Codex, and OpenCode are running there.</p><p>On Android, I use an SSH client called Termius. I connect to the server and run whatever I need.</p><h3>Keeping sessions alive across disconnects</h3><p>Connecting via SSH from a phone means dealing with constant disconnects, which happen often when traveling. If an agent is running directly in the SSH shell, the process dies the second the connection drops.</p><p>The fix is <a href="https://github.com/tmux/tmux/wiki">tmux (Terminal Multiplexer)</a>, a tool that manages multiple terminal sessions and keeps processes running in the background. I run all my agents inside tmux sessions. If I lose my connection, the work doesn&#8217;t stop. I just reconnect and pick up exactly where I left off.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Jxy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Jxy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 424w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 848w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 1272w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Jxy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png" width="1456" height="655" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:655,&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_!_Jxy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 424w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 848w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.png 1272w, https://substackcdn.com/image/fetch/$s_!_Jxy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e44ed49-5947-4071-8281-918708a20dcd_1770x796.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"><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"><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"><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"><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>But once I started using tmux heavily, I ran into a new problem. Typing tmux commands from a phone is painful. The standard commands look like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;9f5f3728-f603-46e2-b36b-b57f360c2e50&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">tmux new-session -s some-long-session-name
tmux attach -t some-long-session-name</code></pre></div><p>With long session names, that is impossible from a phone.</p><p>So I built <a href="https://github.com/alexeygrigorev/tmuxctl">tmuxctl</a> to fix this:</p><ul><li><p><code>t</code> lists all sessions</p></li><li><p><code>t 1</code> attaches to the session with index 1</p></li><li><p><code>t -</code> creates or attaches to a session named after the current folder</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_!_5eo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_5eo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 424w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 848w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 1272w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_5eo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png" width="1080" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1080,&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_!_5eo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 424w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 848w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.png 1272w, https://substackcdn.com/image/fetch/$s_!_5eo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e6fda37-c008-46e7-bf10-e41253de3d41_1080x756.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"><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"><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"><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"><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>t</code> listing all current sessions on the server, ready to attach by index</figcaption></figure></div><p>I also use Makefiles for everything that requires more than a few characters of typing. I always liked Makefiles, but the phone makes them mandatory.</p><h3>Launching agents with two keystrokes</h3><p>With sessions staying alive, the next bottleneck is launching agents.</p><p>Typing out the full invocation for any of these from a thumb keyboard is too slow. I&#8217;ve set up short aliases:</p><ul><li><p><code>csp</code>: Claude with skip permissions</p></li><li><p><code>cy</code>: Codex with --yolo mode</p></li><li><p><code>oc</code>: OpenCode</p></li></ul><p>Two or three keystrokes and the agent is running.</p><p>&#8220;Skip permissions&#8221; and &#8220;YOLO&#8221; sound reckless. But I understand the risks: these agents are running on an isolated remote machine with no access to production environments.</p><p>I try to be very careful when granting my agents access to production. Maybe you remember the incident I described in <a href="https://alexeyondata.substack.com/p/how-i-dropped-our-production-database">How I Dropped Our Production Database and Now Pay 10% More for AWS</a>?</p><p>I learned the hard way that you shouldn&#8217;t give your agents access to production. They can only reach a sandbox AWS account with a temporary 1-hour session. Real deployments are always triggered through CI, never on the remote machine.</p><p>If an agent does something destructive, the blast radius is limited to this one machine. I can rebuild the entire environment from my bootstrap scripts in minutes. That isolation is what makes these &#8220;reckless&#8221; shortcuts safe to use.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/the-system-i-built-to-ship-code-from?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/p/the-system-i-built-to-ship-code-from?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><h3>Forwarding remote ports automatically</h3><p>The agents are constantly spinning up local servers: a dev environment, a local LLM, or a quick preview tool. To actually see any of that from my phone&#8217;s browser, I need those remote ports forwarded back to my localhost.</p><p>Standard Android SSH apps are a pain for this. They force you to enter ports manually and offer no auto-detection of what&#8217;s currently running. Every time a new port opens, you have to stop and configure it, which completely breaks the flow.</p><p>On my laptop, I&#8217;d already solved this with <a href="https://github.com/alexeygrigorev/ssh-auto-forward">ssh-auto-forward</a>, a Python utility I mentioned in <a href="https://alexeyondata.substack.com/p/5-useful-utilities-i-built-with-ai">5 Useful Utilities I Built with AI Coding Assistants</a>. It just sits there, monitors which ports open on the remote server, and automatically maps them to my local machine.</p><p>I needed that exact same automation on my phone. I handed the Python source to an agent and told it to port the entire logic over to a native Android app.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fQz1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fQz1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 424w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 848w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 1272w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fQz1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&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_!fQz1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 424w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 848w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.png 1272w, https://substackcdn.com/image/fetch/$s_!fQz1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07493962-56a8-45e0-a3b6-a04fe17b2870_1794x978.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"><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"><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"><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"><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 created <a href="https://github.com/alexeygrigorev/ssh-auto-forward-android">ssh-auto-forward-android</a> in Kotlin. I can understand Kotlin a bit because I used to be a Java developer, and Kotlin looks similar to Java. But I have no intention to read this code, just like I never looked inside the Python version. It works the way I need it to, and that&#8217;s enough for 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_!i-tM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i-tM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 424w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 848w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 1272w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i-tM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png" width="536" height="461.98331788693235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:930,&quot;width&quot;:1079,&quot;resizeWidth&quot;:536,&quot;bytes&quot;:211918,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i-tM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 424w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 848w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.png 1272w, https://substackcdn.com/image/fetch/$s_!i-tM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bc43eed-a25a-4ca3-8890-57dda31b0a4e_1079x930.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"><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"><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"><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"><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 Android app listing the ports it is forwarding from the remote ports to localhost</figcaption></figure></div><p>I open the app, tap Connect, and see the list of forwarded ports. Tapping a port row opens the browser on localhost.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xmdt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xmdt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 424w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 848w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 1272w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xmdt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png" width="498" height="577.3991323210412" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1069,&quot;width&quot;:922,&quot;resizeWidth&quot;:498,&quot;bytes&quot;:178844,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xmdt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 424w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 848w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.png 1272w, https://substackcdn.com/image/fetch/$s_!Xmdt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0be6ea63-a9c0-409c-8d73-753feed4634f_922x1069.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"><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"><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"><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"><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">Talking to a Qwen2.5-Coder 14B server (llama.cpp on port 8030) after tapping the forwarded-port row</figcaption></figure></div><h2>Talking to the agent instead of typing</h2><p>Even with custom shortcuts and <code>tmuxctl</code>, typing any prompt on a phone is a nightmare. So I stopped typing and started talking to the agent instead.</p><p>My primary tool for this is an Android keyboard called Typeless. I open a session, trigger dictation, and just talk. Typeless processes my stream of thought into clean, structured text and drops it right into the terminal input.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mnkq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mnkq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 424w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 848w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 1272w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mnkq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png" width="404" height="478.05206073752714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1091,&quot;width&quot;:922,&quot;resizeWidth&quot;:404,&quot;bytes&quot;:338256,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Mnkq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 424w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 848w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.png 1272w, https://substackcdn.com/image/fetch/$s_!Mnkq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79cb5113-1ed9-4978-9eac-5df98c750fa4_922x1091.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"><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"><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"><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"><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 long prompt dictated into Claude Code via Typeless</figcaption></figure></div><p>Typeless has one problem: I&#8217;m on the free version and hit usage limits. When that happens, I fall back to Android&#8217;s built-in voice recognition. It doesn&#8217;t work well for the terminal, and the input is messy, but the agents mostly understand what I mean.</p><p>When I need to record something longer, I switch to Google Recorder. I open the app, record what I want to say, tap Share, and create a public link. I sent that link to the agent. The agent uses <a href="https://github.com/alexeygrigorev/.claude/blob/main/skills/fetch-google-recorder/SKILL.md">a skill</a> to download the file and transcribe it. Finally, it uses the content as instructions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R9xD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R9xD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 424w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 848w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 1272w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R9xD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png" width="418" height="499.6052060737527" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1102,&quot;width&quot;:922,&quot;resizeWidth&quot;:418,&quot;bytes&quot;:259392,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R9xD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 424w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 848w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.png 1272w, https://substackcdn.com/image/fetch/$s_!R9xD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a2c0964-ef28-4637-bcd5-af4af7959643_922x1102.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"><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"><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"><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"><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">Google Recorder&#8217;s share menu. I use &#8220;Create a link to view on recorder.google.com&#8221; to hand the recording to the agent.</figcaption></figure></div><p>I like Google Recorder because it works offline too &#8211; also on planes. When I&#8217;m on a plane, I open my laptop to review the code or run the local version and click around. While doing it, I dictate feedback to my phone, and share the recording with the agents when I land.</p><h2>Looking at things the phone can&#8217;t show</h2><p>Everything I&#8217;ve described so far is text in, text out. The phone handles that flow perfectly. But it&#8217;s still not ideal for visual work, like reviewing screenshots or choosing between design variants. SFTP on a phone is too inconvenient for these checks.</p><p>For visual decisions, I ask agents to build throw-away tools. Recently, I needed to select banners for the AI Shipping Labs site. Reviewing 30 different variants from a phone just wasn&#8217;t going to happen from my phone.</p><p>So I asked Codex to create a small HTML page where I can review the visuals, reject the ones I don&#8217;t like and add comments. I opened it on my phone, made my selections, tapped &#8220;Export Picks,&#8221; and handed that file back to the agent to finalize the task.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GOlN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GOlN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 424w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 848w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 1272w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GOlN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png" width="358" height="568.0629067245119" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1463,&quot;width&quot;:922,&quot;resizeWidth&quot;:358,&quot;bytes&quot;:656807,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GOlN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 424w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 848w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.png 1272w, https://substackcdn.com/image/fetch/$s_!GOlN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F524bc4ee-a537-46d7-914b-8914f85d7f97_922x1463.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"><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"><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"><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"><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 throwaway banner picker: 30 variants of the Community Launch banner, with like / neutral / reject and per-variant comments</figcaption></figure></div><p>If I had to wait to review the banners on a laptop, I would have postponed it for many days. But instead, Codex built me a small throw-away app, and I finished reviewing all 30 variants in 5 minutes.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/the-system-i-built-to-ship-code-from/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/the-system-i-built-to-ship-code-from/comments"><span>Leave a comment</span></a></p><h2>Behind the scenes: how this article was made</h2><p>So far I&#8217;ve talked about communicating with agents using my phone. But a large part of my work is also writing. I rely a lot on the <a href="https://alexeyondata.substack.com/p/telegram-assistant">Telegram writing assistant</a> for that, especially when I&#8217;m commuting.</p><p>This is how it usually works:</p><ul><li><p>First, I do a brain dump with all my ideas. I record voice messages, send links, and share screenshots with the Telegram bot. Writing this article took me around 40 minutes while I was commuting to the gym.</p></li><li><p>Then I process the dump using the /process command, which creates a draft.</p></li><li><p>After that, I open the article and record my feedback into Google Recorder. For this article, I did this after my warm-up set at the gym, continued between sets, and finished when I was going home later in the evening. In total, it took another 30-40 minutes.</p></li><li><p>Then Valeriia does the initial editing pass, we review the article together, and then she finishes it.</p></li></ul><p>We finish the article in 4-5 hours, which would previously have taken days of sitting down to write.</p><p>This lets me share interesting material here and keep trying new things constantly.</p><p>And I want to thank Valeriia for handling all that &#8211; it would never be possible to create all the articles here in my Substack without her involvement.</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>What I&#8217;ve Been Working On Recently</h2><h3>1) Building AI Shipping Labs with Claude Code agents</h3><p>A lot landed on the AI Shipping Labs website this week &#8212; new features, bug fixes, and small additions on top of small additions. Most of it didn&#8217;t come from me typing code. The agents have been running nonstop. One thing I noticed: Codex caps me at 5 concurrent agents, while Claude Code seems to have temporarily lifted that limit.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2H18!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2H18!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2H18!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2H18!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2H18!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2H18!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg" width="496" height="458.3407407407407" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:998,&quot;width&quot;:1080,&quot;resizeWidth&quot;:496,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Claude Code running-tasks panel with multiple SWE and product-manager agents working on AI Shipping Labs website features&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 running-tasks panel with multiple SWE and product-manager agents working on AI Shipping Labs website features" title="Claude Code running-tasks panel with multiple SWE and product-manager agents working on AI Shipping Labs website features" srcset="https://substackcdn.com/image/fetch/$s_!2H18!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2H18!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2H18!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2H18!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4345bc3-d270-49b4-8c87-fe26aaf7712e_1080x998.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"><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"><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"><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"><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">Claude Code running-tasks panel with multiple software-engineer and product-manager agents on the AI Shipping Labs website</figcaption></figure></div><p>I also worked on new personal plans for people who just joined AI Shipping Labs.</p><h3>2) Vector Databases Workshop (DataTalksClub)</h3><div id="youtube2-BC3NsRUNEIg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;BC3NsRUNEIg&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/BC3NsRUNEIg?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>I ran the second workshop in the series updating LLM Zoomcamp content. This one covered vector search. We went hands-on through:</p><ul><li><p>Turning text into embeddings and indexing them</p></li><li><p>Running a semantic search over the FAQ documents</p></li><li><p>Plugging vector search into a RAG pipeline</p></li><li><p>Comparing it to a keyword search, then combining both as a hybrid retrieval</p></li><li><p>Where vector search fails and how retrieval quality changes the final LLM answer</p></li></ul><h3>3) Solving a Real AI Engineer Take-Home Assignment Live (AI Shipping Labs)</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a70H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a70H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 424w, https://substackcdn.com/image/fetch/$s_!a70H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 848w, https://substackcdn.com/image/fetch/$s_!a70H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 1272w, https://substackcdn.com/image/fetch/$s_!a70H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a70H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png" width="1456" height="711" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:711,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:187017,&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/198835715?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.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_!a70H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 424w, https://substackcdn.com/image/fetch/$s_!a70H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 848w, https://substackcdn.com/image/fetch/$s_!a70H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.png 1272w, https://substackcdn.com/image/fetch/$s_!a70H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3a61b7f-f104-4c4c-ae5a-5c75fb5c4219_1970x962.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"><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"><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"><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"><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 picked one actual AI engineer take-home assignment and worked through it live: interpreting the task, clarifying requirements, scoping under time pressure, and making design decisions on the fly.</p><div class="callout-block" data-callout="true"><p>If you want to get the notes from this workshop or watch it in the recording, <a href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_content=2026_05_22">join AI Shipping Labs</a>. Basic plan gets you the notes and Main plan also adds the recording.</p></div><p>This workshop was based on my broader analysis: 2,400+ job descriptions, 100+ GitHub repos with real candidate submissions, and practitioner reports on the interview process. I collected the insights from this analysis at the <a href="https://github.com/alexeygrigorev/ai-engineering-field-guide">AI Engineer Field Guide</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_content=2026_05_22&quot;,&quot;text&quot;:&quot;Join AI Shipping Labs&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_content=2026_05_22"><span>Join AI Shipping Labs</span></a></p><h2>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_!fJcu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fJcu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 424w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 848w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 1272w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fJcu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png" width="1456" height="807" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:807,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:209914,&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/198835715?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.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_!fJcu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 424w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 848w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.png 1272w, https://substackcdn.com/image/fetch/$s_!fJcu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62ba7aa0-c8b6-4498-9ebf-759e471a5b55_1642x910.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"><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"><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"><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"><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://stitch.withgoogle.com/docs/design-md/overview">DESIGN.md</a> is a plain-text design system spec from Google Stitch that AI coding agents read to generate consistent UI. It&#8217;s a markdown file you drop into your project root that covers color palette, typography, components, layout, depth, and dos and don&#8217;ts in a format LLMs parse natively. Think of it as the design counterpart to AGENTS.md: where AGENTS.md tells agents how to build the project, DESIGN.md tells them how it should look and feel.</p></li><li><p><a href="https://github.com/sgl-project/sglang">SGLang</a> is a high-performance LLM serving framework that takes share from vLLM by better supporting prefix reuse. RadixAttention uses a radix tree to automatically identify and cache shared KV blocks across requests, so when multiple users start with the same system prompt, the same RAG document, or the same few-shot examples, those tokens are computed once and reused. The cache hit rates are dramatic: 85&#8211;95% for few-shot workloads vs 15&#8211;25% for vLLM&#8217;s prefix caching. It supports continuous batching, speculative decoding, tensor and expert parallelism, and structured outputs, and is deployed at scale by xAI, LinkedIn, Cursor, and others.</p></li><li><p><a href="https://github.com/mastra-ai/mastra">Mastra</a> is a TypeScript-first agent framework from the team behind Gatsby, bundling agents, workflows, RAG, integrations, and evals into a single cohesive package built on the Vercel AI SDK. Its standout feature is Observational Memory, a text-based memory system that reduces token costs by 4-10x while outperforming RAG on long-context benchmarks by compressing text by 3-6x and tool outputs by 5-40x. It hit 1.0 in January 2026 with over 300k weekly npm downloads, and ships a local playground for testing agents before deployment.</p></li></ul><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[11 Workshops to Build Production AI Agents (RAG, MCP, Guardrails & Deployment) in One Place]]></title><description><![CDATA[I consolidated all the GenAI workshops I've ever taught into one place. Here's what's inside and how to take them.]]></description><link>https://aishippingblog.com/p/11-workshops-to-build-production</link><guid isPermaLink="false">https://aishippingblog.com/p/11-workshops-to-build-production</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 15 May 2026 15:23:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iG1h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For a while, my Gen AI workshops have been scattered across different places. The recordings are on YouTube. The code is on GitHub. Some workshops I gave at conferences were never recorded, and the materials were stored in my archives, but never went anywhere afterward. Two of the more recent ones were published in Slack for AI Shipping Labs members.</p><p>I decided to collect them all in one place and created the <a href="http://aishippinglabs.com/workshops">Workshops section on the AI Shipping Labs Website</a>. You sign up once and get access to 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_!iG1h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iG1h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 424w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 848w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 1272w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iG1h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png" width="1456" height="921" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:921,&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;: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_!iG1h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 424w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 848w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.png 1272w, https://substackcdn.com/image/fetch/$s_!iG1h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff40f0b2f-3651-4c28-a78b-64925be5e61c_2048x1295.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"><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"><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"><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"><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 post, I&#8217;ll share:</p><ul><li><p>What I did to put the workshops in one place, and how this fits into the larger AI Shipping Labs platform update</p></li><li><p>How I reformatted the materials so they are easier to follow</p></li><li><p>The full list of workshops on the site, grouped by topic</p></li><li><p>How access works: what&#8217;s free and what&#8217;s part of membership</p></li><li><p>That AI Hero is now on the site too, in a new format</p></li></ul><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>How I Worked on this Update</h2><p>The workshops library is one piece of a larger update to AI Shipping Labs. To explain what changed for the workshops, I have to start with the platform.</p><h3>The First Version of the Website and Why We Needed an Update</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sWaO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sWaO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 424w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 848w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 1272w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sWaO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png" width="1456" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&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_!sWaO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 424w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 848w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.png 1272w, https://substackcdn.com/image/fetch/$s_!sWaO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcec6c551-10e3-40b5-b624-28a881e5641f_1456x875.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"><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"><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"><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"><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 Next.js version of the AI Shipping Labs website with different sections</figcaption></figure></div><p>Until recently, AI Shipping Labs ran on a simple Next.js site that Valeriia built with v0 and Cursor. It was a landing page with a few resource pages, a newsletter form, and a checkout. As a first version, it was the right call as we wanted to see whether people would sign up before we built anything heavier. And more than 80 people did, by the way!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bUSL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bUSL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 424w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 848w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 1272w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bUSL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png" width="1456" height="921" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:921,&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_!bUSL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 424w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 848w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.png 1272w, https://substackcdn.com/image/fetch/$s_!bUSL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4b9e0a5-5cac-4af7-998d-bcb17e559b33_2048x1296.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"><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"><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"><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"><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">User profile view on the current version of the AI Shipping Labs website</figcaption></figure></div><p>But this first version had no platform behind it. There was no way to gate content by membership tier, no way to register for events, no progress tracking, and no member profiles. If we wanted the community to have any of that, we needed a more sophisticated platform. So I started building one on Django, and I&#8217;ve been migrating the site piece by piece. The full story is in a separate newsletter:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;57dd8a21-1727-42f9-bc9c-94724fe1fea5&quot;,&quot;caption&quot;:&quot;In the previous newsletter, Valeriia and I announced AI Shipping Labs, a community for people who want to learn AI by building their projects, with a clear plan, support from other practitioners, and regular check-ins to keep moving forward.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&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 We Built AI Shipping Labs Website using AI Tools&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;id&quot;:11546144,&quot;name&quot;:&quot;Valeriia Kuka&quot;,&quot;bio&quot;:&quot;Content Manager @ DataTalksClub. Passionate about AI/ML. I write, edit, and grow AI/ML content to help people learn. Curious about how we explain and scale tech&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bef4ed47-29a3-4356-bc6a-6e131d611f70_2819x2819.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-04-17T17:48:42.473Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dada0e3b-b9d5-4ded-90e0-42d310adb7c9_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/how-we-built-ai-shipping-labs&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:194540481,&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><h3>Workshop Library Idea</h3><p>The workshop library is the first major chunk of content to land on the new platform.</p><p>The first reason for that is consolidation. Instead of recordings on YouTube, code on GitHub, and notes scattered across various READMEs, everything is now in one place with a single sign-up.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E3EA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E3EA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 424w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 848w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 1272w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E3EA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png" width="1456" height="646" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:646,&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_!E3EA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 424w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 848w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.png 1272w, https://substackcdn.com/image/fetch/$s_!E3EA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe00e34e7-ed89-4256-9f35-664dd7fc2ea1_2048x909.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"><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"><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"><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"><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 repository with some of the workshop&#8217;s code</figcaption></figure></div><p>The less obvious reason is the format. Once the workshops are on our platform, each workshop&#8217;s shape can change. They don&#8217;t have to be long README files anymore. They can be proper tutorials, with parts, navigation, progress tracking, and comments under each section.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Eny!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Eny!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 424w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 848w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 1272w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Eny!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png" width="1456" height="843" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:843,&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_!8Eny!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 424w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 848w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.png 1272w, https://substackcdn.com/image/fetch/$s_!8Eny!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3efc64bd-3daa-47a6-80b7-910fa47b9f7e_2048x1186.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"><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"><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"><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"><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">Initial format of the workshop notes with code</figcaption></figure></div><p>Each workshop used to come with one long README: code, text, links, screenshots, all on a single page. That format works if you already know the material and you&#8217;re using the README as a reference. It doesn&#8217;t work if you&#8217;re trying to learn the workshop from scratch because it&#8217;s hard for you to navigate where you are in the progression of the workshop. And there&#8217;s no natural stopping point that would allow you to approach a 1.5 h workshop in several learning sessions. You&#8217;re either reading the whole thing in one sitting, or you&#8217;re losing your place.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3QvS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3QvS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 424w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 848w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3QvS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png" width="1456" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.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_!3QvS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 424w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 848w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!3QvS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f37ede4-a125-4ca0-ab46-7c6d1a8eb8fd_2048x1122.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"><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"><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"><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"><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">Current format of the workshop notes: I transformed them into structured tutorials, with parts, navigation, progress tracking, and comments under each section</figcaption></figure></div><h3>How I Reformatted Workshops into Structured Tutorials</h3><p>I asked Claude Code to handle the reformatting. For each workshop, it took the existing README and turned it into a multi-part tutorial:</p><ul><li><p>Split the content into logical sections</p></li><li><p>Lifted the headings into a structure that fits the platform</p></li><li><p>Added Mermaid diagrams where the architecture or data flow was easier to visualize than to describe.</p></li></ul><p>I reviewed each result, fixed what didn&#8217;t land, and moved on. The agent did the repetitive structural work. I spent my time on the parts that needed judgment.</p><p>This is consistent with how I work on the platform overall. The only difference is that for bigger features like auth, access tiers, the event system, and the course pages, I use <a href="https://alexeyondata.substack.com/p/i-built-an-ai-agent-team-for-software">my agent team</a> rather than a single 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_!dm0r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dm0r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 424w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 848w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 1272w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dm0r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png" width="1456" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&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_!dm0r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 424w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 848w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.png 1272w, https://substackcdn.com/image/fetch/$s_!dm0r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82cc2af1-a2b0-408c-9332-52f7b7886c02_1456x722.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"><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"><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"><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"><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 pipeline: every task goes through PM, SWE, QA, and back to PM before commit</p><ul><li><p>A Product Manager agent grooms the task into a spec with acceptance criteria.</p></li><li><p>A Software Engineer implements it.</p></li><li><p>A Tester verifies it against the criteria.</p></li><li><p>The PM does a final acceptance review before anything gets committed.</p></li></ul><p>I wrote up the setup in detail here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;3ae1c2ee-ec41-4861-bbd1-3333d548b032&quot;,&quot;caption&quot;:&quot;Over the past few weeks, I&#8217;ve been trying out a new way of working with agent teams for software development using Claude Code. Instead of just seeing it as a single tool, I&#8217;ve started thinking of the main session as an orchestrator that directs a small team of agents.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&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;I Built an AI Agent Team for Software Development and Tested on 5 Real Projects&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-04-03T10:46:22.930Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!QESf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F732331c7-693b-46cb-9053-7161a130d3d6_2779x1378.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/i-built-an-ai-agent-team-for-software&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:193047033,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:31,&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>The workshop&#8217;s reformatting was a smaller, more contained job, so a single agent was enough. But the same agent team loop runs in the background for the rest of the site.</p><p>For task tracking, I use GitHub Issues. Each time a new issue is on the platform, the agent team processes it and implements necessary changes. I then review the changes and add comments or new issues to further improve the result.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b1ii!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b1ii!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 424w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 848w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 1272w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b1ii!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png" width="1456" height="973" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:973,&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_!b1ii!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 424w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 848w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.png 1272w, https://substackcdn.com/image/fetch/$s_!b1ii!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492587cf-8a29-44ef-bcb3-8b10e4764bf1_1456x973.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"><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"><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"><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"><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 issues as task tracker</figcaption></figure></div><p>This is similar to how I initially started shipping features, using my smartphone to dictate issues on the go and using GitHub Copilot to implement what I needed. Full story is here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;54d4be28-cb7a-462a-ab01-6bb5ac1dcba9&quot;,&quot;caption&quot;:&quot;Hi everyone,&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&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;Shipping Features from my Smartphone with GitHub Copilot&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;2025-12-26T13:06:15.873Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!WOB0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08443bbe-7ce5-4ff2-bf56-40f845c65eb2_1920x1424.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/shipping-features-from-a-tram-stop&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:182081359,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&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><h2>Workshops on the Site</h2><p>You can find the full list of 11 workshops on the <a href="https://aishippinglabs.com/workshops">AI Shipping Labs website</a>.</p><p>Most of the workshops come with a video recording (some are from conferences and only include structured notes), structured notes broken into multiple parts, and links to related materials. Most are free with a sign-up. But the deployment workshops are part of membership.</p><p>Here&#8217;s what&#8217;s on the site, grouped roughly in the order you&#8217;d take them if you were starting from scratch.</p><h3>Foundations: Search and RAG</h3><p>Before agents, there&#8217;s retrieval. These two workshops cover the building blocks on which everything else depends.</p><p>1. <strong><a href="https://aishippinglabs.com/workshops/build-your-own-search-engine">Build Your Own Search Engine</a></strong>. Build a search engine from scratch over the DataTalks.Club Zoomcamp FAQ documents. Start with TF-IDF text search, add cosine similarity for ranking, then extend it with embedding-based semantic search. This is the foundation underneath every RAG system and most agents &#8212; once you&#8217;ve built search yourself, the rest of the stack stops feeling like magic.</p><p>2. <strong><a href="https://aishippinglabs.com/workshops/agentic-rag">Introduction to RAG and Agents</a></strong>. Build a classic RAG pipeline: index documents and retrieve highlighted snippets. Implement search() (returns snippets) and get_file (returns the full document). Let the LLM decide which tool to call and wire the tools into an agent. The final agent mirrors how humans read docs: search, scan snippets, open the promising one, use it to answer the initial question.</p><h3>Building Agents</h3><p>If you&#8217;ve never built an agent before, this is where to start. Each workshop builds a working agent from first principles and then evolves it, so you can see what each layer of abstraction actually gives you.</p><p>3. <strong><a href="https://aishippinglabs.com/workshops/building-ai-agents-mcp-pydanticai-openai">Building AI Agents with MCP, PydanticAI and OpenAI</a></strong>. Start with a plain search() function and a course FAQ dataset. Build an agent from raw OpenAI Responses API calls. Then rebuild the same agent with the OpenAI Agents SDK, then with PydanticAI, then expose the tools via MCP so they&#8217;re reusable across agents and IDEs like Cursor.</p><p>4. <strong><a href="https://aishippinglabs.com/workshops/building-coding-agent-python-django">Build a Coding Agent: Python/Django Edition</a></strong>. Build a coding agent that scaffolds and modifies Django apps from a plain-language prompt. The agent uses a small set of file-system tools and a carefully designed system prompt.</p><p>5. <strong><a href="https://aishippinglabs.com/workshops/coding-agent-skills-commands">Skills.md from Scratch: Build a Skill-Driven Coding Agent</a></strong>. Extend a basic coding agent into a general-purpose one using <em>skills</em> (modular capabilities loaded on demand) and <em>commands</em> (explicit user-facing shortcuts). The interesting bit: you reimplement Claude Code-style patterns in Python yourself, so you can see exactly how skills and commands work under the hood instead of treating them as a black box.</p><p>6. <strong><a href="https://aishippinglabs.com/workshops/coding-agent-v2">Coding Agent with Skills (2026 update)</a></strong>. Build a coding agent from scratch: tool calls, an agentic loop, a skills system that loads reusable instruction files on demand, and a few practical patterns for keeping the agent under control. This is an updated version of the workshop that combines content from workshops 4 and 5, so I covered both building a coding agent and using skills and commands.</p><p>7. <strong><a href="https://aishippinglabs.com/workshops/youtube-ai-agent-temporal">Build a Production-Ready YouTube AI Agent with Temporal</a></strong>. A deep-research agent over years of DataTalks.Club podcast transcripts. The focus of this workshop is on ingestion reliability and system design. By the end, you have an agent that answers questions over years of audio content with a fault-tolerant ingestion layer underneath.</p><h3>Safety</h3><p>Once your agent works, the next question is what happens when someone uses it badly. This is the only workshop in this group right now, and it&#8217;s a deep one.</p><p>8. <strong><a href="https://aishippinglabs.com/workshops/guardrails-for-ai-agents">Building Safe AI Agents with Guardrails</a></strong>. Take a working FAQ assistant and protect it with input and output guardrails, such as blocking off-topic questions and preventing the agent from making promises it shouldn&#8217;t (e.g., deadline extensions). The workshop ends with a framework-agnostic async pattern that runs guardrails in parallel and cancels the agent the moment a check fails. It works with any agent framework.</p><h3>Deployment (Members-Only)</h3><p>These two workshops are the most recent ones, and they cover deployment: getting from a working notebook to something running in production. Both are part of <a href="https://aishippinglabs.com/pricing">AI Shipping Labs membership</a>.</p><p>9. <strong><a href="https://aishippinglabs.com/workshops/end-to-end-agent-deployment">End-to-End Agent Deployment</a></strong>. Take a FAQ chatbot from a Jupyter notebook to production. Start with a notebook that calls the OpenAI Responses API with a single search tool. Wrap it in FastAPI. Add a vanilla JS frontend with SSE streaming. Containerize with Docker. Deploy to Railway. Set up a GitHub Actions CI/CD pipeline. Each step is done alongside a coding agent, and the prompts are included verbatim so you can reproduce the workflow with any agent.</p><p>10. <strong><a href="https://aishippinglabs.com/workshops/lambda-agent-deployment">Deploying an Agent to AWS Lambda</a></strong>. Start from the FastAPI service deployed to Railway in the previous workshop. Strip out FastAPI. Replace it with a custom AWS Lambda runtime that handles both the static frontend and the streaming agent API. Ship everything as a single container image, deployed as a Lambda Function URL with SSE streaming. Most of the code is written by a coding agent (Codex), with the exact prompts quoted. Because this was a freestyle session, it also surfaces a lot of meta-discussion: how to work with agents, when to trust them, and when to slow down and read the code yourself.</p><h3>AI-Assisted Development</h3><p>One workshop, but a useful reference point if you&#8217;re trying to figure out which AI dev tool to actually use.</p><p>11. <strong><a href="https://aishippinglabs.com/workshops/ai-coding-tools-compared">AI Coding Tools Compared</a></strong>. Compare ChatGPT, Claude, Claude Code, GitHub Copilot, Cursor, Lovable, and AI agents on one task: build a Snake game in React. You&#8217;ll understand the AI assistant categories: chat apps, IDE assistants, CLI agents, and project bootstrappers, so you can pick the right one for the task in front of you.</p><h2>How to Access Workshops</h2><p>Go to <a href="https://aishippinglabs.com/workshops">aishippinglabs.com/workshops</a>, pick the workshop that best fits what you&#8217;re building right now, and start there. Sign up, and you have access.</p><p>Most workshops are free. The two most recent ones, <strong>End-to-End Agent Deployment</strong> and <strong>Deploying an Agent to AWS Lambda</strong>, are part of the <a href="https://aishippinglabs.com/pricing">AI Shipping Labs membership</a>. Written materials for both are included in the Basic tier (&#8364;20/month). Basic also gives you access to the rest of the paid library: exclusive articles, AI tool breakdowns, and research notes, which are still WIP, but we&#8217;ll soon start publishing the first pieces.</p><p>But if you want to get recordings of these workshops as well, they&#8217;re only available under Main and Premium plans. These plans also include community access, group coding sessions, mini-courses, and personal teardowns of resumes, LinkedIn, and GitHub.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bL30!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bL30!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 424w, https://substackcdn.com/image/fetch/$s_!bL30!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 848w, https://substackcdn.com/image/fetch/$s_!bL30!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 1272w, https://substackcdn.com/image/fetch/$s_!bL30!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bL30!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png" width="1456" height="828" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:828,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:323146,&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/197840273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.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_!bL30!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 424w, https://substackcdn.com/image/fetch/$s_!bL30!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 848w, https://substackcdn.com/image/fetch/$s_!bL30!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.png 1272w, https://substackcdn.com/image/fetch/$s_!bL30!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3bd183f-2248-4ab3-a176-75543089dd07_2530x1438.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"><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"><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"><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"><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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/workshops&quot;,&quot;text&quot;:&quot;Sign up for free&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/workshops"><span>Sign up for free</span></a></p><h3>Why are the open workshops behind a sign-up?</h3><p>First, it lets you track your progress in your profile and mark specific parts as &#8220;Completed.&#8221; Second, we collect sign-ups so we know who is using the materials, and we can stay in touch with you and get feedback in the comments section.</p><h2>AI Hero is on the Website Too</h2><p>While I was working on the workshops, I also moved <a href="https://aishippinglabs.com/courses/aihero">AI Hero</a> to the site.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xw73!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xw73!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 424w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 848w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 1272w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xw73!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png" width="1456" height="567" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:567,&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_!Xw73!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 424w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 848w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.png 1272w, https://substackcdn.com/image/fetch/$s_!Xw73!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bb6f569-15af-46c3-a344-e58daf31da05_2048x797.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"><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"><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"><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"><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>AI Hero is a free seven-day crash course on building a complete AI agent: RAG, search, function calling, evaluation, and web deployment, all applied to any GitHub project.</p><p>The course used to arrive in your inbox, one lesson per email per day. The email format had limits. There was no way to track which lessons you&#8217;d completed. If you had a question on day 3, your only option was to email me. If you wanted to come back to day 5 three weeks later, you had to dig through your inbox.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y14Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y14Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 424w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 848w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 1272w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y14Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png" width="1456" height="901" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:901,&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_!Y14Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 424w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 848w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.png 1272w, https://substackcdn.com/image/fetch/$s_!Y14Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb37d466c-d336-459c-9e04-ef5797acd5b5_2048x1267.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"><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"><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"><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"><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>Now it&#8217;s a proper course on the site. Each of the seven days is its own page. Every lesson has a comments section where you can ask questions. You can track your progress from your profile. There&#8217;s a certificate at the end, with a project submission and peer review step to earn it. It&#8217;s still free.</p><p>If you&#8217;ve been meaning to try it, this is a better way to take it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/courses/aihero&quot;,&quot;text&quot;:&quot;Sign up for AI Hero&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/courses/aihero"><span>Sign up for AI Hero</span></a></p><h2>What I&#8217;ve Been Working On Recently</h2><h3>1) Sprint 1 at AI Shipping Labs Continues</h3><p>We&#8217;re moving through the first sprint at AI Shipping Labs along with 27 participants. This Wednesday, we had our 2nd weekly live standup where participants shared progress, insights, and blockers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Qep!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Qep!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 424w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 848w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 1272w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Qep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png" width="1456" height="837" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:837,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:913026,&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/197840273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.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_!1Qep!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 424w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 848w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.png 1272w, https://substackcdn.com/image/fetch/$s_!1Qep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb691cd1-6a0e-4115-abe3-766c9f33203e_2410x1386.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"><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"><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"><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"><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>Their projects cover a wide range. Some people are shipping their first end-to-end AI project. Others are operationalizing something they already built: adding evals, deploying a recommender, and putting CI/CD and monitoring in place around an existing project. A good chunk is building domain-specific RAG systems, like chat with your notes and syllabus ingestion from PDFs.</p><h3>2) DataTalks.Club Admin UI Redesign with Codex</h3><p>Last week, I asked a team of Codex agents to update the DataTalks.Club course management platform: migrate the UI to Tailwind and refresh the design. We iterated on the UI together, and I&#8217;m quite satisfied with the results. There was also some work on the internal admin part &#8212; course admin is now even easier.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fxIA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fxIA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 424w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 848w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fxIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg" width="1280" height="678" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:678,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Redesigned DataTalks.Club course management platform homepage showing Active Courses with LLM Zoomcamp 2026&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="Redesigned DataTalks.Club course management platform homepage showing Active Courses with LLM Zoomcamp 2026" title="Redesigned DataTalks.Club course management platform homepage showing Active Courses with LLM Zoomcamp 2026" srcset="https://substackcdn.com/image/fetch/$s_!fxIA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 424w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 848w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!fxIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e61328b-23b1-41d7-9e90-87b82d40c2cc_1280x678.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"><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"><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"><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"><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">Redesigned DataTalks.Club site after the Codex + Tailwind pass, with LLM Zoomcamp 2026 highlighted under Active courses</figcaption></figure></div><p>I&#8217;m taking advantage of Codex&#8217;s 2x limits while I can, so I&#8217;ve been running things in parallel. I was traveling last week and managed to do all of this on my phone while enjoying the Harz Mountains.</p><h3>3) DataTalks.Club Migrated to Rustkyll</h3><p>I finally migrated the DataTalks.Club site to <a href="https://github.com/alexeygrigorev/rustkyll/">Rustkyll</a> &#8212; a fast, drop-in replacement for Jekyll, written in Rust. It reads the same source files as Jekyll (Markdown with YAML front matter, Liquid templates, YAML data files, collections) and produces equivalent HTML output, just much faster.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3SFW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3SFW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 424w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 848w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 1272w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3SFW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png" width="1456" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:193595,&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/197840273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.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_!3SFW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 424w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 848w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.png 1272w, https://substackcdn.com/image/fetch/$s_!3SFW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78946991-876b-4fb2-b918-ccb845ae1759_1788x1022.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"><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"><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"><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"><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 local build went from about 2 minutes to roughly 1 second, a ~120x speedup. In GitHub Actions, the build itself went from 22 seconds to about 1 second, a 20x improvement. The CI workflow as a whole only got about 1.5-2x faster overall because most of the CI time is spent on container setup and other prep, not the build itself. Most of the 20x build win is hidden behind CI overhead.</p><p>Still, locally, the build is much faster, which makes iterating on the site much less painful.</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>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_!yV_S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yV_S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 424w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 848w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 1272w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yV_S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png" width="1456" height="609" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:609,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!yV_S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 424w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 848w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.png 1272w, https://substackcdn.com/image/fetch/$s_!yV_S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F013a814a-f7b2-4cd5-85db-c53d33d047dc_1754x734.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"><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"><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"><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"><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 href="https://github.com/garrytan/gstack">gstack</a> is a collection of opinionated Claude Code slash commands that transform a single AI assistant into a team of specialists.</figcaption></figure></div><ul><li><p><a href="https://github.com/garrytan/gstack">gstack</a> is a collection of opinionated Claude Code slash commands that transform a single AI assistant into a team of specialists, such as a CEO, an engineering manager, a release engineer, and a QA engineer. Developed by Y Combinator president Garry Tan, it offers commands like /plan-ceo-review for product thinking, /review for thorough code review, /ship for one-command PR creation, and /browse and /qa for automated browser-based testing with screenshots. It serves as a helpful reference for structuring Claude Code custom commands for multi-role development workflows.</p></li><li><p><a href="https://github.com/Vaibhavs10/insanely-fast-whisper">Insanely Fast Whisper</a> is an opinionated CLI tool that transcribes audio files on-device using OpenAI&#8217;s Whisper models, powered by Hugging Face Transformers, Optimum, and Flash Attention 2. It can transcribe 150 minutes of audio in under 2 minutes on an Nvidia A100, supporting batched inference, word timestamps, and speaker diarization via Pyannote. It works on NVIDIA GPUs and Apple Silicon Macs, supporting multiple Whisper checkpoints, including distil-whisper variants.</p></li></ul><h2>Resource</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g2RN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g2RN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 424w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 848w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 1272w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!g2RN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png" width="1456" height="650" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:650,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!g2RN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 424w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 848w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.png 1272w, https://substackcdn.com/image/fetch/$s_!g2RN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60f2eae1-535a-4885-babc-a091eefa0885_1788x798.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"><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"><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"><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"><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 href="https://github.com/ashishps1/learn-ai-engineering">Learn AI Engineering</a> is a curated collection of free courses, articles, tutorials, and videos.</figcaption></figure></div><p><a href="https://github.com/ashishps1/learn-ai-engineering">Learn AI Engineering</a> is a curated collection of free courses, articles, tutorials, and videos that teach AI and LLMs from scratch. It covers fundamentals, ML, deep learning, generative AI, LLMs, prompt engineering, RAG, agents, and MCP, sourcing from Coursera, Hugging Face, deeplearning.ai, and Stanford. With nearly 5,000 GitHub stars, it offers a structured path for anyone interested in AI engineering without paid courses.</p><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[Building an AI Book Generator with a Plan-Then-Execute Pipeline]]></title><description><![CDATA[How a niche bedtime-book project became a system for generating chapters, audio, EPUB files, and Amazon KDP print artifacts.]]></description><link>https://aishippingblog.com/p/building-an-ai-book-generator-with</link><guid isPermaLink="false">https://aishippingblog.com/p/building-an-ai-book-generator-with</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Fri, 08 May 2026 14:47:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_Ywx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My child has very specific interests and information requests. He asks me to find books on narrow topics like metals or signal sirens, and most of the time, such books don&#8217;t exist. Or they exist, but not as kids&#8217; books.</p><p>So I built a system to generate such books for him. It all started with an experiment with ChatGPT. When I validated the need for a more automated, complex approach, this project evolved into an <a href="https://github.com/alexeygrigorev/ai-book-generator">automated pipeline that uses AI to generate the entire book</a>.</p><p>In this post, I&#8217;ll share:</p><ul><li><p>How the first book about metals started the project</p></li><li><p>Why direct ChatGPT generation was not enough</p></li><li><p>How I used coding agents to create the first books</p></li><li><p>Why I later built a dedicated book generator</p></li><li><p>How the current pipeline plans, writes, formats, and packages books</p></li><li><p>How audio, EPUB, and Amazon KDP outputs are generated</p></li><li><p>What happened when I tried to publish one of the books on Amazon</p></li></ul><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>The First Request: a Book About Metals</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Ywx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Ywx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 424w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 848w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 1272w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Ywx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png" width="1456" height="916" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:916,&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;: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_!_Ywx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 424w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 848w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.png 1272w, https://substackcdn.com/image/fetch/$s_!_Ywx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54414ccb-f6bd-4b12-813b-6ce6d6579c9b_2048x1288.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"><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"><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"><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"><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>His first request was a book about metals. He wanted to know what metals are and how they are used. He only cared about a specific list: palladium, tin, magnesium, titanium, beryllium, lithium, and tungsten. He told me exactly which properties to describe. ChatGPT suggested adding a few other metals to complete the picture, and my son agreed. We planned one chapter per metal to cover its history, extraction, and uses.</p><p>We could not find a book like this, and he wanted to listen to it at bedtime. I first tried generating the book directly with ChatGPT. We iterated on a table of contents. I asked the model to write the first chapter, but the result was very poor. ChatGPT alone didn&#8217;t work.</p><p>I then considered coding agents. They are good at planning and executing. My normal approach to building applications is to iterate on a design with ChatGPT and delegate the implementation to a coding agent. I figured the same pattern should work here. I could use ChatGPT to generate the table of contents and let the coding agent write the actual book.</p><h2>Iterating with Coding Agents on the Metals Book</h2><p>My son and I finalized the outline. I handed the plan to the coding agent. The first version was rough. The agent wrote bullet points instead of prose. Coding agents are likely tuned for documentation rather than narrative writing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z4zj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z4zj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 424w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 848w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z4zj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png" width="356" height="477.4161260426321" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1447,&quot;width&quot;:1079,&quot;resizeWidth&quot;:356,&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_!Z4zj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 424w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 848w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4zj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F164a4d74-42c3-4df3-9c8e-6c0958fb02e0_1079x1447.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"><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"><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"><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"><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">Part of the table of contents of the metals book showing one chapter per metal, with descriptive subtitles like &#8220;gold: metal of pharaohs and pirates&#8221; or &#8220;tungsten: the highest melting point&#8221;.</figcaption></figure></div><p>I tightened the prompt and explicitly asked for normal prose. I managed the process just like my regular coding projects. I created a GitHub issue, let the agent work on it, reviewed the result, and created the next issue. This is the same approach I use when coding from my smartphone. I described it in one of my previous newsletters:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;85b440a1-41c2-4fbf-af38-e7457f8c04a9&quot;,&quot;caption&quot;:&quot;Hi everyone,&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&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;Shipping Features from my Smartphone with GitHub Copilot&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;2025-12-26T13:06:15.873Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!WOB0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08443bbe-7ce5-4ff2-bf56-40f845c65eb2_1920x1424.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://alexeyondata.substack.com/p/shipping-features-from-a-tram-stop&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:182081359,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&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><p>Another problem was context limits. The agent didn&#8217;t finish the task. If I asked it to rewrite the whole book in proper prose, it would rewrite the first five chapters and stop because &#8220;it was tired.&#8221; I had to push the agent to finish the entire text repeatedly.</p><p>In parallel, I asked the agent to set up automation. I wanted to publish the book as a website first so I could read it in the browser. I added EPUB output later simply because I found it interesting. The automation worked well. The text for the metals book required a lot of manual guidance, but my child liked the final result.</p><div class="callout-block" data-callout="true"><p>The published book lives at <a href="https://alexeygrigorev.com/little-book-of-metals-ru/">https://alexeygrigorev.com/little-book-of-metals-ru/</a>. Source code: <a href="https://github.com/alexeygrigorev/little-book-of-metals-ru">github.com/alexeygrigorev/little-book-of-metals-ru</a>.</p></div><h2>Second Book: Gallium and Potassium Alloys</h2><p>Halfway through the metals project, my son decided he was only interested in two specific metals: gallium and potassium. He specifically wanted to read about their alloy because it has unusual properties. We made a separate book for that topic.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1A7i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1A7i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 424w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 848w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 1272w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1A7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png" width="1265" height="704" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:704,&quot;width&quot;:1265,&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_!1A7i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 424w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 848w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.png 1272w, https://substackcdn.com/image/fetch/$s_!1A7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F658e75dc-ae90-4e48-82a9-23dd574fb842_1265x704.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"><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"><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"><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"><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>By that point, I understood the workflow. I put exactly what I wanted directly into the first prompt. The experience from the metals book paid off immediately. The book came out well on the first attempt. The entire project required just two GitHub issues. One issue was to write the book. The second issue was to add EPUB and MOBI publishing.</p><p>The two closed issues in the gallium-kalium book repository: &#8220;&#1053;&#1072;&#1087;&#1080;&#1089;&#1072;&#1090;&#1100; &#1082;&#1085;&#1080;&#1075;&#1091;&#8221; (write the book) and &#8220;epub and mobi generation&#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_!pevx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pevx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 424w, https://substackcdn.com/image/fetch/$s_!pevx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 848w, https://substackcdn.com/image/fetch/$s_!pevx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 1272w, https://substackcdn.com/image/fetch/$s_!pevx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pevx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png" width="1080" height="636" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:636,&quot;width&quot;:1080,&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_!pevx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 424w, https://substackcdn.com/image/fetch/$s_!pevx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 848w, https://substackcdn.com/image/fetch/$s_!pevx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.png 1272w, https://substackcdn.com/image/fetch/$s_!pevx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F229f6b2f-f928-430b-840c-e5cc85c673b8_1080x636.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"><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"><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"><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"><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><div class="callout-block" data-callout="true"><p>Source code: <a href="https://github.com/alexeygrigorev/gallium-kalium-book-ru">github.com/alexeygrigorev/gallium-kalium-book-ru</a>.</p></div><h2>Third Book: Conifers</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0I7z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0I7z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 424w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 848w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 1272w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0I7z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png" width="1277" height="734" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/efe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:734,&quot;width&quot;:1277,&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_!0I7z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 424w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 848w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.png 1272w, https://substackcdn.com/image/fetch/$s_!0I7z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefe8105a-ea07-46b2-8b2e-4a2f5481ad77_1277x734.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"><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"><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"><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"><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>Later, my son wanted to learn more about conifers after studying them in school. By then, the workflow was standard. We discussed the topic with ChatGPT, built a plan, created a GitHub issue, let Copilot work on it, and received the completed book.</p><div class="callout-block" data-callout="true"><p>Source code: <a href="https://github.com/alexeygrigorev/conifers-book-ru">github.com/alexeygrigorev/conifers-book-ru</a>.</p></div><h2>Building a Specialized Book Agent</h2><p>After the third book, I realized a general coding agent like Copilot was not the best tool. I decided to build a dedicated program for two reasons. First, I needed a use case for my course participants to show how to build specialized agent systems. Second, I suspected a specialized agent would produce better narrative text than models tuned for code.</p><p>I built the program to compare GPT, Claude, and a newly released Gemini model. My evaluation was informal. I simply read the books with my child and picked the best text. Gemini produced the best results.</p><p>The hypothesis proved correct in practice. The specialized agent wrote better prose than the general coding agents.</p><p>The workflow settled into a standard plan-then-execute pattern, identical to what I teach for code:</p><ol><li><p>Iterate in a chat interface to finalize the table of contents.</p></li><li><p>Convert the chat output into a structured YAML plan containing chapters, sections, and bullet points.</p></li><li><p>Run a loop over the plan using <code>for chapter in plan: generate_chapter(...)</code>. Each iteration receives a compressed summary of prior chapters to maintain context without passing the entire text.</p></li></ol><h2>Many Books Since</h2><p>I started by redoing the metals book just to test the new pipeline. Since then, the system has successfully handled several highly specific requests. These included a book on warning sirens, a guide to how fireworks work, and a book dedicated entirely to cable-driven mechanisms such as funiculars.</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>How the Book Generator Works</h2><p>The <a href="http://github.com/alexeygrigorev/ai-book-generator">repository</a> provides an end-to-end pipeline for planning and generating books. It starts with an interactive planning step, converts the plan into a typed YAML file, generates chapters from that plan, and then produces audio, EPUB, and Amazon Kindle Direct Publishing print artifacts.</p><p>You can use the system through either a Streamlit interface or command-line scripts. The main design pattern is plan first, then execute. The book structure is decided upfront, and the later generation and publishing steps use that structure as their source of truth.</p><h3>1. Plan Generation</h3><p>The first stage is interactive because the system treats the book structure as something to review before text generation begins.</p><p>You launch the Streamlit interface with <code>make ui</code>.</p><p>In the UI, you enter a topic and select a book size, such as Small, Medium, or Large. Then you start a chat with Gemini 3 Pro Preview. The model streams a draft book plan into the interface.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uYGj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uYGj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 424w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 848w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 1272w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uYGj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png" width="1121" height="475" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:475,&quot;width&quot;:1121,&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_!uYGj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 424w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 848w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.png 1272w, https://substackcdn.com/image/fetch/$s_!uYGj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28ce7b-8900-4757-8bd7-64b5cda37c5b_1121x475.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"><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"><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"><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"><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 Streamlit app interface that you can see after running <code>make ui</code></figcaption></figure></div><p>You can refine the plan through chat. Each refinement call sends the current plan, along with your new feedback, back to Gemini, so the model updates the existing outline rather than starting from scratch.</p><p>When the outline looks good, you click: &#8220;<code>Ready - Create Structured Plan</code>&#8221;. This moves the workflow from freeform planning to structured execution. For headless runs, there is a command-line alternative. You run <code>uv run python -m chapter_based.plan -p books/mybook/input.txt</code> to build the plan from a text file instead of the Streamlit chat.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S8JJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S8JJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 424w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 848w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 1272w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S8JJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png" width="1456" height="753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61fe3af2-726c-492d-96de-d479d284f737_1772x916.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:753,&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_!S8JJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 424w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 848w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.png 1272w, https://substackcdn.com/image/fetch/$s_!S8JJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61fe3af2-726c-492d-96de-d479d284f737_1772x916.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"><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"><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"><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"><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>2. Structured Plan Creation</h3><p>The freeform chat plan is useful for humans, but the generator needs a predictable structure. The next step converts the outline into a typed plan that the execution scripts can read.</p><p>The system sends the finalized plan to Gemini one more time and requests JSON output using <code>response_mime_type=&#8221;application/json&#8221;</code> and a schema generated from the Pydantic <code>BookPlan</code> model. The structured output is saved to <code>books/&lt;slug&gt;/plan.yaml</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_!3AV-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3AV-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 424w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 848w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 1272w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3AV-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png" width="1456" height="713" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:713,&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_!3AV-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 424w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 848w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.png 1272w, https://substackcdn.com/image/fetch/$s_!3AV-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52032518-a3c0-42b4-bf2d-7ef1613f0580_2006x982.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"><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"><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"><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"><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>Here how a slice of the YAML structure looks like:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;24e36b2d-9ce1-4c34-b844-a2e40926f2e2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">name: My Book
slug: my-book
book_language: ru
parts:
  - name: Part One
    introduction: ...
    chapters:
      - name: Chapter Name
        bullet_points:
          - point one
          - point two</code></pre></div><p>This <code>plan.yaml</code> file is the contract between planning, writing, audio generation, and publishing. The writing scripts use it to generate chapters. The publishing scripts use it for metadata and structure. The cover pipeline uses it for back-cover text.</p><p>This separation is important because it allows the planning interface to change without rewriting the execution pipeline, provided it still produces the same structured plan.</p><p>Per-part introductions and back-cover text also come from fields already present in the plan and don&#8217;t require extra model calls during generation.</p><h3>3. Chapter-by-Chapter Execution</h3><p>Once <code>plan.yaml</code> exists, the writing scripts no longer need the chat history. They use the structured plan as the source of truth.</p><p>If you use the command line, you run <code>uv run python -m chapter_based.execute mybook</code>. There is also a Makefile command: <code>make generate-book</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_!pkO_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pkO_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 424w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 848w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1272w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png" width="1456" height="716" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.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;: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_!pkO_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 424w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 848w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1272w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.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"><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"><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"><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"><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 repository contains two generation implementations:</p><ul><li><p><code>book_generator/</code>: section-based generation. It uses more LLM calls per chapter and gives finer-grained control, which is useful for longer books.</p></li><li><p><code>chapter_based/</code>: chapter-based generation. It makes one call per whole chapter and usually produces 3000 to 5000 words per chapter.</p></li></ul><p>The chapter-based path is the cleaner illustration of the pipeline.</p><p>The script <code>chapter_based/execute.py</code> loads the YAML file, flattens the parts into a single list of chapter specifications, and iterates through them.</p><p>For each chapter, the script builds a book progress string with the full chapter list:</p><ul><li><p>Completed chapters are marked with <code>[x]</code>.</p></li><li><p>The current chapter is marked with an arrow.</p></li><li><p>Remaining chapters are marked with <code>[ ]</code>.</p></li></ul><p>Gemini receives this outline together with the bullet points for the current chapter. It does not receive the full text of previous chapters. Cohesion comes from the upfront plan, not from passing actual prior chapter text into each generation call.</p><p>This makes the quality of the initial plan important. The plan needs enough structure to keep chapters aligned across the whole book.</p><h3>4. Audio Generation</h3><p>After the markdown chapters exist, the same book folder can be used to generate audio versions of the content. I added that part because my son sometimes wants to listen to the books rather than read them. The implementation uses Gemini text-to-speech because its voice quality was better than that of the previous setup used in a separate AI bedtime stories project.</p><p>You can start text-to-speech generation with <code>make tts BOOK=...</code> The script <code>book_generator/tts.py</code> calls <code>models/gemini-2.5-flash-preview-tts</code> and uses the default voice, <code>Charon</code>. The script wraps the returned PCM audio into a WAV file and uploads it directly to an S3 bucket.</p><p>Generation runs in parallel with <code>ThreadPoolExecutor</code>. It also uses a cost lock and a skip-if-already-generated check, so existing audio files aren&#8217;t regenerated unnecessarily.</p><p>A separate script, <code>scripts/convert_wav_to_mp3.py</code>, converts WAV files to MP3. It uses <code>ffmpeg</code> and produces MP3 derivatives for distribution.</p><h2>Publishing Process</h2><p>The publishing scripts also use the generated markdown and the same <code>plan.yaml</code>, so EPUB and print output don&#8217;t depend on how the text was generated.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pkO_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pkO_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 424w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 848w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1272w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png" width="1456" height="716" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.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;: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_!pkO_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 424w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 848w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.png 1272w, https://substackcdn.com/image/fetch/$s_!pkO_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2d38e70-bf15-46cb-96ed-00756bcf35b9_1676x824.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"><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"><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"><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"><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>There are three main publishing commands.</p><ol><li><p><code>make ebook BOOK=...</code> publishes the book to EPUB. <code>scripts/convert_to_ebook.py</code> aggregates the markdown files and adjusts heading levels so chapter headings fit the EPUB structure. It then calls Pandoc with title, author, language metadata, and an optional cover image. The output is an EPUB file.</p></li><li><p><code>make kdp-interior BOOK=...</code> publishes the book to KDP interior PDF. <code>scripts/create_kdp_interior.py</code> aggregates the markdown files and renders it through XeLaTeX inside a Docker image for reproducibility. The output is a KDP-ready interior PDF with 6 by 9 inch trim size, mirror margins, gutter, DejaVu fonts for Cyrillic, and a generated table of contents. The resulting file is kdp_interior.pdf.</p></li><li><p><code>make kdp-cover BOOK=...</code> publishes the book to KDP cover PDF. <code>scripts/create_kdp_cover.py</code> builds a wraparound print cover with ReportLab. It lays out the back cover, spine, front cover image, and bleed area. The back-cover description comes from plan.yaml. The spine width is calculated from the page count using pages * 0.0025 inches. The resulting file is kdp_cover.pdf.</p></li></ol><h2>Cost Tracking and Operational Conventions</h2><p>The generator tracks cost during planning and writing because book-length generation can involve long contexts, multiple chapter calls, retries, and optional asset generation.</p><p>The function <code>calculate_gemini_3_cost</code> uses the November 2025 Gemini pricing tiers, including the standard tier and the over-200k-context tier. It also bills &#8220;thoughts&#8221; tokens as output tokens.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E_ts!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E_ts!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 424w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 848w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 1272w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E_ts!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png" width="1456" height="711" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:711,&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_!E_ts!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 424w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 848w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.png 1272w, https://substackcdn.com/image/fetch/$s_!E_ts!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bda2357-fca8-441e-b483-df0db5105fca_1814x886.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"><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"><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"><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"><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 <code>CostTracker</code> accumulates cost during generation. In the Streamlit UI, the running total is shown live while the plan is drafted and refined. During text generation, the terminal output also reports incremental costs as chapters and sections are produced.</p><p>Because generation can take time and cost money, the system writes each artifact to a predictable location and checks whether it already exists before regenerating it.</p><p>Chapter output is written to paths like <code>books/&lt;slug&gt;/part_01/01_chapter.md</code>.</p><p>A typical book folder contains:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;33e7fafb-1501-4e4a-9fce-9b28cb8b9405&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">books/&lt;slug&gt;/
  plan.yaml
  back_cover.md
  cover.jpg
  part_01/
    01_chapter.md
    02_chapter.md
  part_02/
    01_chapter.md
  book.epub
  kdp_interior.pdf
  kdp_cover.pdf</code></pre></div><p>The scripts infer where to read and write files based on naming conventions such as <code>books/&lt;slug&gt;/</code>, <code>part_XX/</code>, and predictable artifact names.</p><p>The generation script checks whether a chapter file already exists. If it does, the script skips that chapter on rerun. A <code>_ready</code> sentinel file in the book folder marks the book as complete and tells the system not to modify it anymore. Per-step <code>*_exists</code> checks prevent the system from regenerating artifacts that already exist.</p><p>All later steps use the same book folder and the same <code>plan.yaml</code>. Planning, writing, audio generation, and publishing are connected through files rather than through one large process. This design choice and operational convention make the pipeline idempotent and resumable: if a run is interrupted, it can continue from the missing pieces instead of starting from the beginning.</p><p>The Makefile wires the workflow together:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;4b3133d0-c57f-4803-8011-e38e93e1aeb6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">make ui

  -&gt; chat and create structured plan
  -&gt; make generate-book
  -&gt; make tts BOOK=...
  -&gt; make ebook BOOK=...
  -&gt; make kdp-interior BOOK=...
  -&gt; make kdp-cover BOOK=...</code></pre></div><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/building-an-ai-book-generator-with?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Alexey On Data! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/building-an-ai-book-generator-with?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/p/building-an-ai-book-generator-with?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h2>Example Generation Run</h2><p>During generation, the terminal output shows what the system is working on and how much each step costs. This makes the generator easier to monitor during long runs. It also makes cost visible while the book is still being produced, rather than only after completion.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oSsg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oSsg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 424w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 848w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 1272w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oSsg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png" width="892" height="534" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:534,&quot;width&quot;:892,&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_!oSsg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 424w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 848w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.png 1272w, https://substackcdn.com/image/fetch/$s_!oSsg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51006a08-e0f4-4238-a0a4-8d19b1827c67_892x534.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"><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"><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"><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"><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 example was a fireworks book generated with this system. It produced 21 chapters, took about 45 minutes of wall-clock time, and cost roughly $4 using Gemini 3 Pro.</figcaption></figure></div><p>This number is useful as a benchmark, but it should not be treated as a fixed estimate. The final cost depends on the model, book length, context size, retries, and which optional steps are included. Gemini Flash has not been tested yet for this workflow, so it isn&#8217;t clear how much quality or cost would change with a cheaper model.</p><h3>Cost and Quality</h3><p>A fully generated book costs less than $5 in one run. The exact number depends on the book and the generation settings, but it gives a practical order of magnitude. The system currently uses Gemini 3 Pro for book generation. This is more expensive than using a smaller or faster model, but in these experiments, the output quality justified the cost. For a personal book to be read repeatedly, that cost may be acceptable.</p><p>The quality was good enough from the early experiments to make the project worth continuing. Books about metals and sirens were useful at home and readable enough. That observation led to the publishing experiment. If the books were useful privately, it was reasonable to test whether they could also be packaged for external readers.</p><h2>Publishing Experiment and Project Value</h2><p>The project started as a way to generate books for private reading. Over time, the quality was good enough to raise another question: could these books be prepared for actual publication?</p><p>That changed the scope of the pipeline. It was no longer enough to generate markdown chapters; it needed to produce files that matched real publishing requirements: EPUB for ebooks, a formatted interior PDF for print, and a full-wrap cover PDF for Amazon KDP.</p><p>That is why I added the publishing scripts, <code>convert_to_ebook.py</code>, <code>create_kdp_interior.py</code>, and <code>create_kdp_cover.py</code>, I mentioned above to test the full publishing flow end to end. This test validated the production pipeline, but it didn&#8217;t validate demand.</p><p>The technical pipeline can produce a book package, but publishing files isn&#8217;t the same as selling a book. The book I tested has been on Amazon for about five months. So far, it has sold zero copies.</p><p>Uploading a generated book to Amazon isn&#8217;t enough on its own. Selling requires a separate layer of work: market research, niche selection, positioning, search optimization, metadata, reviews, and promotion.</p><p>The project is still useful, but its current value is mostly personal and technical. It works as a system for generating books for private reading and as a practical experiment in automated publishing. Turning it into a commercial workflow would require audience research and marketing work outside the generator itself.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippingblog.com/p/building-an-ai-book-generator-with?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippingblog.com/p/building-an-ai-book-generator-with?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><h2>What I&#8217;ve Been Working On Recently</h2><h3>1. AI Shipping Labs First Sprint</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Thux!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Thux!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 424w, https://substackcdn.com/image/fetch/$s_!Thux!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 848w, https://substackcdn.com/image/fetch/$s_!Thux!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 1272w, https://substackcdn.com/image/fetch/$s_!Thux!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Thux!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png" width="1456" height="701" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:701,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:434457,&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/196904163?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.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_!Thux!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 424w, https://substackcdn.com/image/fetch/$s_!Thux!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 848w, https://substackcdn.com/image/fetch/$s_!Thux!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.png 1272w, https://substackcdn.com/image/fetch/$s_!Thux!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d6a6edc-54da-4b51-8425-37046a8df079_2010x968.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"><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"><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"><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"><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 launched Sprint 1 at AI Shipping Labs.</p><p>It&#8217;s a 6-week accountability sprint where members work on their projects, meet weekly, share progress, discuss blockers, and receive feedback.</p><p>For this sprint, Valeriia and I created 25+ personal plans. Each plan is based on the person&#8217;s background, goals, and current situation.</p><h3>2. Freestyle Workshop at AI Shipping Labs</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!86_G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!86_G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 424w, https://substackcdn.com/image/fetch/$s_!86_G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 848w, https://substackcdn.com/image/fetch/$s_!86_G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 1272w, https://substackcdn.com/image/fetch/$s_!86_G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!86_G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png" width="1456" height="801" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:801,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:469744,&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/196904163?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.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_!86_G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 424w, https://substackcdn.com/image/fetch/$s_!86_G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 848w, https://substackcdn.com/image/fetch/$s_!86_G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.png 1272w, https://substackcdn.com/image/fetch/$s_!86_G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1da7c11d-e35b-4336-b11a-5bc9317d93a4_2518x1386.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"><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"><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"><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"><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 also ran another freestyle workshop at AI Shipping Labs.</p><p>In this format, people come with their own ideas or problems, and we try to implement something together. This time, we looked at how to deploy an app to AWS Lambda.</p><p>I wanted to figure this out for a while. Services like Render are convenient, but they are not really serverless. If you deploy something there, the server runs continuously, and you keep paying for it.</p><p>For small, low-traffic projects, it doesn&#8217;t always make sense. Lambda is a better fit because it runs only when needed.</p><p>I upload recordings and notes from my workshops to the <a href="https://aishippinglabs.com/">new AI Shipping Labs website</a>. To get full access to workshop materials, join the community.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_campaign=ai_shipping_labs&amp;utm_content=2026_05_08&quot;,&quot;text&quot;:&quot;Join AI Shipping Labs&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_campaign=ai_shipping_labs&amp;utm_content=2026_05_08"><span>Join AI Shipping Labs</span></a></p><h3>3. New Website for AI Shipping Labs</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TRk4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TRk4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 424w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 848w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TRk4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png" width="1456" height="646" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:646,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:217452,&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/196904163?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.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_!TRk4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 424w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 848w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!TRk4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bdd7bd-e183-4b45-a3a3-8ef8a21cfd0f_2514x1116.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"><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"><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"><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"><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 finally migrated the AI Shipping Labs website from Next.js to Django. There is still a lot to do, but the <a href="https://aishippinglabs.com/">new Django version is now live</a>.</p><p>You can already sign up for free, track your progress, and access some free resources, like the AI Hero course.</p><h3>4. Data Makers Fest in Porto</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q90-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q90-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 424w, https://substackcdn.com/image/fetch/$s_!q90-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 848w, https://substackcdn.com/image/fetch/$s_!q90-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 1272w, https://substackcdn.com/image/fetch/$s_!q90-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q90-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png" width="1280" height="960" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:960,&quot;width&quot;:1280,&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_!q90-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 424w, https://substackcdn.com/image/fetch/$s_!q90-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 848w, https://substackcdn.com/image/fetch/$s_!q90-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.png 1272w, https://substackcdn.com/image/fetch/$s_!q90-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b37e699-1027-422f-9c3f-5de24cf5485c_1280x960.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"><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"><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"><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"><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 was at Data Makers Fest in Porto this week.</p><p>On Monday, I ran a workshop called &#8220;Introduction to Agentic RAG.&#8221; For this workshop, I took the first two modules of LLM Zoomcamp on RAG and agents and simplified them as much as possible into a stripped-down introduction to agentic RAG.</p><p>The difficulty level was intentionally introductory because it works well for people new to the topic who want to understand the main pieces first.</p><p>The next day, I joined a panel in the morning and answered questions from the audience. Later, I moderated a session on production LLMs.</p><p>I also recorded several interviews with people at the conference. The podcast episode based on these interviews is still in preparation and should go out in a couple of weeks.</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>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_!gHxX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gHxX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 424w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 848w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 1272w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gHxX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png" width="1456" height="542" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:542,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:166621,&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/196904163?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.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_!gHxX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 424w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 848w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.png 1272w, https://substackcdn.com/image/fetch/$s_!gHxX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc141a86-f802-4b37-ba44-f2042f57d030_1766x658.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"><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"><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"><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"><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/tirth8205/code-review-graph">Code Review Graph</a> is an MCP-based tool that builds a local knowledge graph of your codebase so AI coding assistants read only what matters during code reviews. It uses Tree-sitter to parse your code into a structural map of functions, classes, imports, and their relationships, then performs blast-radius analysis on changes to identify the minimal set of affected files. The tool supports 19 languages, updates incrementally in under 2 seconds, and benchmarks show an average 8.2x reduction in tokens compared to naive full-file reads.</p></li><li><p><a href="https://github.com/santifer/career-ops">Career-Ops</a> is an open-source, AI-powered job search system built on Claude Code that turns your terminal into a full job search command center. It evaluates job offers using a structured scoring system, generates ATS-optimized, tailored CVs as PDFs, automatically scans 45+ company career portals, and tracks everything in a single pipeline. The author used it to evaluate 740+ job listings, generate 100+ personalized CVs, and land a Head of Applied AI role.</p></li></ul><h2>Resource</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MAcr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MAcr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 424w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 848w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 1272w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MAcr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png" width="1456" height="769" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:769,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2370473,&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/196904163?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.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_!MAcr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 424w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 848w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.png 1272w, https://substackcdn.com/image/fetch/$s_!MAcr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feff23570-02b6-4959-9180-c0cb517b385d_2578x1362.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"><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"><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"><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"><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://codingchallenges.substack.com/">Coding Challenges</a> is a weekly newsletter by John Crickett that publishes hands-on project challenges designed to help software engineers level up by building real-world tools. Each issue walks through rebuilding well-known software from scratch - things like awk, gzip, Docker, an ebook reader, a Loom clone, or AI agents and coding assistants - giving you a concrete, scoped project to practice on. With over 92,000 subscribers and 100+ challenges in the archive, it is a great source of project ideas for anyone who learns best by doing.</p><div><hr></div><p>Edited by <a href="https://www.linkedin.com/in/valeriia-kuka/">Valeriia Kuka</a></p>]]></content:encoded></item><item><title><![CDATA[9 Real-Life AI Projects from AI Engineering Buildcamp Graduates]]></title><description><![CDATA[This is what the students of the second cohort built + AI Shipping Labs new]]></description><link>https://aishippingblog.com/p/9-real-life-ai-projects-from-ai-engineering</link><guid isPermaLink="false">https://aishippingblog.com/p/9-real-life-ai-projects-from-ai-engineering</guid><dc:creator><![CDATA[Alexey Grigorev]]></dc:creator><pubDate>Sat, 02 May 2026 12:01:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/4k_3vOINbgM" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The second cohort of my AI Engineering course finished a few weeks ago, and in this post I want to share with you what we built. The projects are real-life and include personal assistants, professional tooling, and health-case assistants.</p><p>Some of us presented the projects live a few weeks ago on the demo day, so we&#8217;ll start with them.</p><div id="youtube2-4k_3vOINbgM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;4k_3vOINbgM&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/4k_3vOINbgM?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><h3>1) bAIpacking Agent by Eduardo Gonzalo Almorox</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!H-xY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!H-xY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 424w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 848w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 1272w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!H-xY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png" width="1428" height="959" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:959,&quot;width&quot;:1428,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:181384,&quot;alt&quot;:&quot;alt text&quot;,&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;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="alt text" title="alt text" srcset="https://substackcdn.com/image/fetch/$s_!H-xY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 424w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 848w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.png 1272w, https://substackcdn.com/image/fetch/$s_!H-xY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F162b3879-74ec-44e4-b004-99c9f744a68c_1428x959.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"><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"><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"><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"><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>Edu&#8217;s&nbsp;<a href="https://github.com/edugonzaloalmorox/baikpacking-agent">bAIpacking Agent</a>&nbsp;helps long&#8209;distance cyclists decide which tires, drivetrain, and bags to use for specific races. The agent scrapes event reports from <a href="https://dotwatcher.cc/">DotWatcher</a> (a platform for bike races), stores them in PostgreSQL, and builds a RAG pipeline.</p><p>On the backend side, FastAPI resolves the event name, finds similar events, retrieves rider setups, and summarizes evidence. Then the results are presented via a Reflex UI.</p><p>Evaluation runs offline: each recommendation call is logged and later scored by a judge script.</p><p></p><h3>2) Meal&#8209;Map by Pavlo Skorodziievskyi</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V-d1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V-d1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 424w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 848w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 1272w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V-d1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png" width="1456" height="1360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1360,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;215FD3BC-D69B-421E-B74E-4B318712F5D3&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="215FD3BC-D69B-421E-B74E-4B318712F5D3" title="215FD3BC-D69B-421E-B74E-4B318712F5D3" srcset="https://substackcdn.com/image/fetch/$s_!V-d1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 424w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 848w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.png 1272w, https://substackcdn.com/image/fetch/$s_!V-d1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59d966c5-4281-4294-936d-3b61296d3dca_2882x2692.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"><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"><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"><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"><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>Pavlo&#8217;s <a href="https://github.com/elgrassa/CapstoneMealMapSimplified">Meal&#8209;Map</a> is a meal planner for an entire family. It takes into account dietary preferences, goals and allergies. The output is a list for grocery shopping as well as a list of recipes.</p><p>The knowledge base consists of 4 public&#8209;domain nutrition sources chunked into 28 documents. The agent interacts with the knowledge base using 9 tools such as nutrition lookup, recipe search, and meal&#8209;plan generation.</p><p>Evaluation uses a hand&#8209;crafted ground-truth set, an LLM judge, and a search parameter sweep to tune retrieval. The agent limits calls to 20 LLM requests per session and $0.50 of cost per day; once exceeded, it downgrades to deterministic logic.</p><p></p><h3>3) Engineering Decision Memory Agent by Camila Gaitan Mosquera</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p4nq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p4nq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 424w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 848w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 1272w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p4nq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f245de07-fadc-4b83-846f-bf5154e73016_1678x937.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:982506,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.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_!p4nq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 424w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 848w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.png 1272w, https://substackcdn.com/image/fetch/$s_!p4nq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff245de07-fadc-4b83-846f-bf5154e73016_1678x937.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"><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"><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"><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"><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>Engineering Decision Memory Agent helps engineering teams recover context from past technical decisions. It indexes ADRs (architecture decision records), RFCs (request for comments), and postmortems so engineers can ask questions like &#8220;Why did we move from a monolith to microservices?&#8221; or &#8220;Why was Kafka chosen over SQS?&#8221;</p><p>The flow for the demo:</p><ul><li><p>Camila indexed around 21 documents.</p></li><li><p>The system split them into overlapping chunks, embeded them with a Hugging Face model, stored them in ChromaDB.</p></li><li><p>On user query, the system retrieves the most relevant chunks for a question, and passes them to Claude as context.</p></li><li><p>Claude returns a structured JSON response that maps directly to UI cards: decision, alternatives considered, tradeoffs, context and constraints, and source documents.</p></li></ul><p>Camila also built a monitoring dashboard with queries per day, answers found vs. not found, latency distribution, token usage, interaction history, and basic thumbs-up feedback. Her next step is to rebuild the same agent with <a href="https://github.com/github/spec-kit">spec-kit</a> to define clearer requirements, constraints, and implementation details.</p><p></p><h3>4) VoiceIssue Agent by Mladen Maric</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qmIC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qmIC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 424w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 848w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 1272w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qmIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png" width="1456" height="1023" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1023,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1585528,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.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_!qmIC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 424w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 848w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.png 1272w, https://substackcdn.com/image/fetch/$s_!qmIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75b8355e-b055-4e94-936f-6da39c96e319_1496x1051.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"><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"><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"><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"><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 <a href="https://github.com/lomodev-mmaric/voice_issue_code">VoiceIssue Agent</a> lets users create GitHub issues via voice through Telegram.</p><p>When you have a lot of projects, keeping track of ideas is difficult, especially when you&#8217;re away from your computer. The agent simplifies this process by letting you use voice notes: you simply dictate your idea, and the bot assigns it to appropriate project. Before making any actions, the bot asks for a confirmation. </p><p>Everything is deployed via Docker Compose. Additionally, services include an interface for managing secrets, a session dashboard connected to the PostgreSQL log database, and a Grafana dashboard for analyzing cost and latency. There&#8217;s also an LLM&#8209;as&#8209;judge container that scores each session to filter out low-quality requests.</p><p></p><h3>5) Medical Transcription App by Spyros Koumarianos</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2CFN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2CFN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 424w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 848w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 1272w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2CFN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png" width="974" height="289" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:289,&quot;width&quot;:974,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69968,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.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_!2CFN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 424w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 848w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.png 1272w, https://substackcdn.com/image/fetch/$s_!2CFN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b1ff55-add1-438e-a0c6-34b3754f6e43_974x289.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"><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"><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"><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"><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>Spyros developed an app for clinicians to turn doctor-patient sessions into actionable <a href="https://www.ncbi.nlm.nih.gov/books/NBK482263/">SOAP notes</a>. This is information is sensitive, so it&#8217;s important to do everything locally. The app transcribes with a local Whisper model, performs basic diarisation (speaker disambiguation), and summarizes the discussion into a SOAP note using a local Llama model.</p><p>Operating entirely offline, doctors can disconnect their computers from the internet and create notes without transmitting audio to any server. Processing a three-minute recording takes around ten minutes on regular hardware without any GPUs.</p><p></p><p>Some of the projects weren&#8217;t presented live on the Demo day. I&#8217;ll describe them here too.</p><h3>6) Cyber Sachet by Nirajan Acharya</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mFSN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mFSN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 424w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 848w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mFSN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png" width="1456" height="776" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:776,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:327699,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.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_!mFSN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 424w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 848w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!mFSN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7546ef35-dea8-4c12-a799-2f7264488d10_2094x1116.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"><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"><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"><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"><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://github.com/nirajanacharya/Cyber-Agent">Cyber Sachet</a> is a bilingual AI assistant for cybersecurity awareness. The agent targets Nepali users who need guidance on phishing, social engineering, and legal penalties. In Nepal, this information is scattered and often available only in English.</p><p>It uses a local knowledge base and selects tools based on the user&#8217;s intent, such as semantic search, law search, or penalty check. Responses are provided in both English and Nepali and include citations.</p><p>It uses Logfire for monitoring, and includes an LLM judge to measure accuracy, precision, and recall. The app is deployed on Streamlit Cloud for evaluation.</p><p></p><h3>7) SnapSplit by James Watkins</h3><p>SnapSplit is an AI&#8209;powered receipt&#8209;splitting app. </p><p>The target users are people who go to restaurants often, and often with the same group of friends. You upload a picture of a restaurant bill, and a vision model extracts the line items. Then the system understands who each dish belongs to and then splits the bill. </p><p>If any mismatches are detected, it re-runs OCR with specific hints and escalates to human review if needed.</p><p></p><h3>8) AMR Awareness Platform by Juan Perez Prim</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!36-D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!36-D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 424w, https://substackcdn.com/image/fetch/$s_!36-D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 848w, https://substackcdn.com/image/fetch/$s_!36-D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 1272w, https://substackcdn.com/image/fetch/$s_!36-D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!36-D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png" width="1456" height="566" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:566,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:155099,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.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_!36-D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 424w, https://substackcdn.com/image/fetch/$s_!36-D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 848w, https://substackcdn.com/image/fetch/$s_!36-D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.png 1272w, https://substackcdn.com/image/fetch/$s_!36-D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88698511-7d27-47c0-90f5-60b4e0454f7a_1784x694.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"><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"><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"><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"><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>Juan&#8217;s <a href="https://github.com/juanpprim/amr_ai">Antimicrobial Resistance (AMR) Awareness Platform</a> educates users about the causes, risks, and prevention of AMR.</p><p>A pipeline ingests data from 15 sources, including WHO, CDC, FAO, PubMed, and Our World in Data, and converts HTML and PDF documents into Markdown. The text is chunked and embedded with BioBERT. Retrieval combines BM25 keyword search with BioBERT semantic search via reciprocal rank fusion.</p><p>A PydanticAI agent, backed by Claude, answers questions with citations via a Gradio interface.</p><p></p><h3>9) AI Regulation Research Assistant by L&#233;onore Tideman</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FO3C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FO3C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 424w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 848w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 1272w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FO3C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png" width="1456" height="679" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:679,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:204835,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.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_!FO3C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 424w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 848w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.png 1272w, https://substackcdn.com/image/fetch/$s_!FO3C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3afcbd82-ab7a-4e9e-890d-17dc11e11144_1770x826.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"><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"><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"><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"><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://github.com/LEMTideman/MyAgent">AI Regulation Research Assistant</a> helps practitioners navigate AI compliance across European, US, and Dutch jurisdictions. </p><p>The pipeline:</p><ul><li><p>A scope guard filters out off-topic prompts.</p></li><li><p>The agent uses a local retrieval system over a curated corpus before performing a web search.</p></li><li><p>The web search utilizes Brave Search combined with Jina Reader to fetch and clean page content.</p></li><li><p>The agent connects to <a href="https://ansvar.eu/">Ansvar</a>&#8217;s external legal data services (EU, US, Dutch, and automotive regulations) and uses them to answer legal questions.</p></li></ul><p>Integration tests ensure the agent routes questions correctly, and an LLM judge scores answers against structured compliance criteria.</p><p></p><p>I&#8217;m really happy to see these projects! We&#8217;ve already started Cohort 3 (now we&#8217;re finishing week 3). I&#8217;m very excited to see what we will build this time!</p><p>If you&#8217;re planning to enroll in the next edition of the course, join the mailing list. I don&#8217;t know when exactly I&#8217;ll run it, but you&#8217;ll be the first to know about it.</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 Mailing List&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 Mailing List</span></a></p><p></p><h2>What I&#8217;ve Been Working On Recently</h2><h3>1. Updated &#8220;Create Your Own Coding Agent&#8221; workshop</h3><p>I went to MLCon Amsterdam last week to give a workshop about building your own coding agent.</p><p>Things change quickly, that&#8217;s why I decided to update <a href="https://dev.aishippinglabs.com/workshops/building-coding-agent-python-django">my old workshop</a> and equip the agent from there <a href="https://dev.aishippinglabs.com/workshops/coding-agent-skills-commands">with skills</a>. The result is <a href="https://github.com/alexeygrigorev/workshops/tree/main/coding-agent-v2">the &#8220;Coding Agent v2&#8221; workshop</a> that I presented in Amsterdam. </p><p>If you want to see more content like that and learn from me, join AI Shipping Labs - we host similar workshops regularly.</p><p></p><h3>2. AI Shipping Labs: New Website + Accountability Sprint </h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cKfJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cKfJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 424w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 848w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 1272w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cKfJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png" width="1456" height="834" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:834,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:423950,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.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_!cKfJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 424w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 848w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.png 1272w, https://substackcdn.com/image/fetch/$s_!cKfJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf899920-44c3-4291-bcd1-7a7ac2ccf884_2580x1478.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"><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"><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"><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"><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 continue working on the Django website for AI Shipping Labs. Right now, I am taking all my AI-related workshops, updating them and posting them there. For now, you can see them on <a href="https://dev.aishippinglabs.com/workshops">the dev version</a> of the website, but soon <a href="http://aishippinglabs.com">aishippinglabs.com</a> will serve this new content too. </p><p>Also, AI Shipping Labs already has 81 members.</p><p>Next week, we start a 6-week accountability sprint. <a href="https://alexeyondata.substack.com/p/tired-of-learning-ai-alone-were-launching">I wrote about the idea earlier</a> and now we&#8217;re ready! It will include weekly check-ins to report progress and blockers. By the end, everyone will finish with a project.  </p><p>For early joiners, we develop a personalized plan based on your goals and background and lay out the exact steps for the sprint. Valeriia and I already created more than 20 plans, and we&#8217;re very excited to see how it goes. </p><p>You can still join us as the sprint just starts.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_campaign=ai_shipping_labs&amp;utm_content=2026_04_24&quot;,&quot;text&quot;:&quot;Join AI Shipping Labs&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://aishippinglabs.com/#tiers?utm_source=alexey_on_data&amp;utm_medium=email&amp;utm_campaign=ai_shipping_labs&amp;utm_content=2026_04_24"><span>Join AI Shipping Labs</span></a></p><p></p><h3>3. Third DataTalks.Club Meetup</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nBwO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nBwO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nBwO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Attendees gather in a modern venue, listening to speakers discussing agentic search and workflows, with presentation visuals displayed behind them.&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="Attendees gather in a modern venue, listening to speakers discussing agentic search and workflows, with presentation visuals displayed behind them." title="Attendees gather in a modern venue, listening to speakers discussing agentic search and workflows, with presentation visuals displayed behind them." srcset="https://substackcdn.com/image/fetch/$s_!nBwO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!nBwO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3709e60f-90ef-49e0-bca7-41eb56e5aa28_1200x675.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"><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"><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"><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"><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>On April 28, we hosted our third offline DataTalks.Club meetup of 2026.</p><p>Thanks to everyone who joined and stayed to discuss. Also, I&#8217;d like to thank Ivan Potapov for organizing the meetup, Zalando for hosting us, and Hopsworks and Elastic for sponsoring the pizza.</p><p>We&#8217;ll continue doing more offline meetups this year!</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>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_!rDUP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rDUP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 424w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 848w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 1272w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rDUP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png" width="1456" height="408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:408,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:83729,&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/196089982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.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_!rDUP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 424w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 848w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.png 1272w, https://substackcdn.com/image/fetch/$s_!rDUP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c7b95e0-71c5-4925-ae16-18a02e0281ec_1462x410.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"><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"><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"><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"><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/HKUDS/DeepTutor">DeepTutor</a> is an open-source AI tutoring assistant that turns your documents into an interactive learning environment. It combines RAG-powered knowledge bases with multiple learning modes - chat, guided learning journeys, quiz generation, deep research, and an AI co-writer - all sharing the same conversation context. It also supports persistent &#8220;TutorBots&#8221; that remember your learning history and evolve over time.</p></li><li><p><a href="https://github.com/ksimback/tech-debt-skill">Tech Debt Audit</a> is a Claude Code skill that produces a thorough, file-cited tech debt audit of an entire codebase rather than a generic best-practices checklist. It runs a three-phase protocol - orient, audit across nine dimensions, deliver - with required file:line citations on every finding and a "looks bad but is actually fine" section that catches shallow analysis. The output is a persistent TECH_DEBT_AUDIT.md artifact you can commit and re-run, with resolved findings tracked over time.</p></li></ul><h2>Resource</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IN44!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IN44!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 424w, https://substackcdn.com/image/fetch/$s_!IN44!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 848w, https://substackcdn.com/image/fetch/$s_!IN44!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 1272w, https://substackcdn.com/image/fetch/$s_!IN44!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IN44!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png" width="1200" height="488" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:488,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Banner&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="Banner" title="Banner" srcset="https://substackcdn.com/image/fetch/$s_!IN44!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 424w, https://substackcdn.com/image/fetch/$s_!IN44!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 848w, https://substackcdn.com/image/fetch/$s_!IN44!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.png 1272w, https://substackcdn.com/image/fetch/$s_!IN44!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba00638f-b057-48f7-a74c-4a62fd449aab_1200x488.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"><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"><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"><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"><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://github.com/Arindam200/awesome-ai-apps">Awesome AI Apps</a> is a collection of 70+ practical examples for building LLM-powered applications, covering everything from simple chatbots to advanced multi-agent systems. The projects span starter agents, RAG applications, MCP integrations, memory-enhanced agents, and production-ready workflows using frameworks like Agno, CrewAI, LangChain, PydanticAI, and AWS Strands. It is a great source of inspiration for AI project ideas with working code you can study and extend.</p>]]></content:encoded></item></channel></rss>