<?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[Developer Tips]]></title><description><![CDATA[Uncover the latest insights, techniques, and strategies for mastering the intersection of AI and programming.]]></description><link>https://developertips.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!YZlf!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6640d165-b93c-46ee-822e-dfd2aabe1215_512x512.png</url><title>Developer Tips</title><link>https://developertips.substack.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 16 Apr 2026 01:39:04 GMT</lastBuildDate><atom:link href="https://developertips.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Marco Franzon]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[developertips@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[developertips@substack.com]]></itunes:email><itunes:name><![CDATA[Marco Franzon]]></itunes:name></itunes:owner><itunes:author><![CDATA[Marco Franzon]]></itunes:author><googleplay:owner><![CDATA[developertips@substack.com]]></googleplay:owner><googleplay:email><![CDATA[developertips@substack.com]]></googleplay:email><googleplay:author><![CDATA[Marco Franzon]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[A Good Idea Is All You Need]]></title><description><![CDATA[The idea is the deliverable.]]></description><link>https://developertips.substack.com/p/a-good-idea-is-all-you-need</link><guid isPermaLink="false">https://developertips.substack.com/p/a-good-idea-is-all-you-need</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Sat, 04 Apr 2026 19:20:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!43k_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Andrej Karpathy published a <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f">gist on GitHub</a>. No library, no framework, no repository with a `package.json` and a CI pipeline. Just a markdown file. A description of a pattern he calls &#8220;LLM Wiki&#8221;, a way to use an LLM as a tireless librarian that builds and maintains a personal knowledge base for you.</p><p>The gist got already  over 500 stars (a few hours later), hundreds of forks, dozens of comments from people already implementing their own versions of it.</p><p>And there isn&#8217;t a single line of code in the entire thing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!43k_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!43k_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!43k_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!43k_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!43k_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!43k_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1025466,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://developertips.substack.com/i/193198324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!43k_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!43k_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!43k_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!43k_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b2bcbf5-97ff-4140-b0a9-38e2032bbfe0_1280x720.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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>What Karpathy Actually Shared</strong></h2><p>The idea is simple and powerful: instead of doing RAG, uploading documents, retrieving chunks, generating answers from scratch every time, you let the LLM <em>incrementally build a wiki</em>. You feed it sources. It reads them, extracts key information, creates pages, updates cross-references, flags contradictions, and maintains the whole thing over time.</p><p>Knowledge compounds instead of being re-derived on every query.</p><p>But here&#8217;s the part that matters for this article. At the bottom of the gist, Karpathy writes:</p><blockquote><p><em>This document is intentionally abstract &#8212; describing the idea, not specific implementation. The right way to use this is sharing it with your LLM agent and working together to instantiate a version fitting your needs. The document only communicates the pattern. Your LLM figures out the rest.</em></p></blockquote><p>Read that again. He&#8217;s not saying &#8220;here&#8217;s a prototype, go build on it.&#8221; He&#8217;s saying: <strong>here&#8217;s an idea, give it to your AI, and it will build it for you.</strong></p><p>The idea <em>is </em>the deliverable.</p><h2><strong>Code Used to Be the Hard Part</strong></h2><p>For decades, the bottleneck in software was implementation. You could have the best idea in the world, but without the ability (or the team, or the funding) to write the code, it stayed in your head. &#8220;Ideas are cheap, execution is everything&#8221; was gospel. And for good reason &#8212; translating a concept into working software required deep expertise, time, and sustained effort.</p><p>That equation is inverting.</p><p>LLMs can write code. Not perfectly, not always, but well enough that a clearly described idea can become a working prototype in minutes. The bottleneck is no longer &#8220;can you build it?&#8221; It&#8217;s &#8220;do you know what to build?&#8221; and, more importantly, &#8220;can you describe it well enough that an AI can build it?&#8221;</p><p>Karpathy didn&#8217;t need to publish a reference implementation. Everyone who reads his gist can paste it into Claude Code, Codex, Cursor, or any agent and say &#8220;build this for me.&#8221; The implementation becomes a conversation between a human and an LLM. What needed to be shared was the <em>*pattern*</em>, the mental model, the architecture, the why.</p><h2><strong>The Spec Is the Software</strong></h2><p>There&#8217;s something quietly radical in Karpathy&#8217;s architecture. His LLM Wiki has three layers: raw sources, the wiki itself, and <em>the schema</em>: a document like `CLAUDE.md` or `AGENTS.md` that tells the LLM how the wiki is structured and what to do.</p><p>That schema is not code. It&#8217;s prose. Natural language instructions. And yet it <em>is </em>the program. It defines behavior, structure, and workflow. It&#8217;s executable, not by a compiler, but by an LLM.</p><p>We&#8217;re watching a new kind of software emerge. One where the specification and the implementation collapse into the same artifact. You write what you want in plain language. The LLM interprets and executes it. The boundary between &#8220;describing the system&#8221; and &#8220;building the system&#8221; dissolves.</p><p>This is why sharing the idea matters more than sharing the code. The code is ephemeral, it&#8217;ll be different for every person, every stack, every LLM. The idea is the durable part.</p><h2><strong>What Humans Are Still For</strong></h2><p>Karpathy puts it plainly:</p><blockquote><p><em>The human curates sources, directs analysis, asks good questions, and thinks about meaning. The LLM does everything else.</em></p></blockquote><p>This is the new division of labor. The LLM handles the bookkeeping, the cross-references, the summaries, the consistency checks, the formatting. The tedious work that makes people abandon wikis, gardens, and knowledge bases. The work that scales linearly with the size of the system and eventually crushes you.</p><p>The human does the part that can&#8217;t be automated: deciding what matters, choosing what to investigate, recognizing when something is interesting, asking the question nobody thought to ask.</p><p>In this world, the most valuable thing you can do is <em>*think clearly and share what you&#8217;ve thought*</em>. Not hoard it until you&#8217;ve built a polished product. Not wait until you have a working demo. Share the idea. Someone, or something, will build it.</p><h2><strong>The Memex Finally Works</strong></h2><p>Karpathy traces his pattern back to Vannevar Bush&#8217;s Memex from 1945, a personal knowledge store with associative trails between documents. Bush imagined everything except who would do the maintenance. Eighty years later, we have the answer: the LLM.</p><p>But the bigger realization is that Bush also couldn&#8217;t solve distribution. His Memex was a private device. You couldn&#8217;t easily share a trail of thought with someone else and have them <em>*build on it*</em> in their own context.</p><p>Now you can. Karpathy shared a trail of thought as a markdown file. Hundreds of people are forking it, adapting it, instantiating it in their own domains, personal health tracking, research, competitive analysis, reading notes. The same idea, running on different data, for different people, built by different AIs.</p><p>This is what the open-source movement looks like when the thing being shared is no longer code but thought.</p><h2><strong>Share Your Ideas</strong></h2><p>The lesson from Karpathy&#8217;s gist is not about wikis or RAG alternatives. It&#8217;s about leverage.</p><p>If you have a clear idea for how something should work: a workflow, a system, a pattern, a tool write it down. Describe the what, the why, the structure. Don&#8217;t worry about the implementation. Don&#8217;t wait until you have time to code it. Don&#8217;t keep it private because &#8220;it&#8217;s just an idea.&#8221;</p><p>Share it. In plain language. As a gist, a blog post, a message to a friend.</p><p>In 2026, a well-articulated idea is a program that anyone with an LLM can run.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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 Age of Disposable Code ]]></title><description><![CDATA[We will never maintain the code again, we will generate new ones as needed.]]></description><link>https://developertips.substack.com/p/the-age-of-disposable-code</link><guid isPermaLink="false">https://developertips.substack.com/p/the-age-of-disposable-code</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Sun, 08 Mar 2026 20:11:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!d7hU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For most of the history of software engineering, code was treated as a long-term asset. Developers were trained to think like architects: build strong foundations, design flexible abstractions, and create systems that could survive years sometimes decades of change.</p><p>Books, conference talks, and engineering cultures revolved around principles such as <strong>reusability</strong>, <strong>clean abstractions</strong>, <strong>modularity</strong>, and <strong>future-proof design</strong>. Code was something you crafted carefully, like infrastructure. Once written, it was expected to support many use cases over time.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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>But the economics of software development have changed.</p><p>Today, a growing portion of code is not written to last. It is written to <strong>solve a problem quickly</strong>, serve its purpose, and often be replaced or discarded later. In many cases, investing time to make code perfectly reusable no longer delivers the return it once did.</p><p>We are entering the age of <strong>disposable code </strong>not careless code, but code whose primary value lies in <strong>solving a specific problem efficiently</strong>, not in being reused forever.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d7hU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d7hU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 424w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 848w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 1272w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d7hU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:697056,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://developertips.substack.com/i/190317109?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d7hU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 424w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 848w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 1272w, https://substackcdn.com/image/fetch/$s_!d7hU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e359e75-1da8-4901-8e0f-4fa6211b7f87_1024x1536.heic 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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 can be generated and destroyed.</figcaption></figure></div><h2><strong>When Writing Code Was Expensive</strong></h2><p>To understand this shift, it helps to remember a time when writing code was genuinely costly.</p><p>In earlier decades, development cycles were slower. Tooling was limited. Infrastructure had to be managed manually. Debugging was harder, deployments were riskier, and distributing software updates was expensive. Every new module required careful design because mistakes were difficult to undo.</p><p>Under those conditions, the logic was simple: <strong>if writing software is expensive, make it reusable</strong>.</p><p>Developers spent large amounts of time designing general solutions that could serve multiple applications. Frameworks were built internally. Shared libraries were carefully maintained. Teams invested in abstraction layers intended to survive for years.</p><p>The effort spent on generalization was justified because rewriting things later would be even more expensive.</p><p>In other words, <strong>engineering for reuse was a rational economic decision</strong>.</p><h2><strong>The Cost of Producing Code Has Collapsed</strong></h2><p>That economic model has been steadily eroding.</p><p>Modern development environments have dramatically reduced the cost of producing working software. High-level languages, powerful frameworks, cloud platforms, package ecosystems, containerization, and continuous deployment pipelines have removed much of the friction that once existed.</p><p>Today, a developer can spin up infrastructure in minutes, integrate complex services through APIs, and rely on vast open-source ecosystems instead of building everything from scratch.</p><p>More recently, AI-assisted development has pushed this trend even further. Code generation tools can produce working prototypes, integrations, and data transformations in seconds.</p><p>When code can be produced this quickly, the economics change.</p><p>If something takes only a few minutes or hours to recreate later, it may not be worth spending extra time today trying to make it universally reusable. In many cases, the effort required to generalize a solution exceeds the cost of simply writing a new one when needed.</p><p>The result is a quiet but profound shift: <strong>code is becoming cheaper than design</strong>.</p><h2><strong>The Hidden Cost of Reusable Systems</strong></h2><p>Ironically, the real cost of software has never been writing it, it has always been <strong>maintaining it</strong>.</p><p>Reusable code must serve many contexts. That means it must anticipate edge cases, handle multiple configurations, remain backward compatible, and stay understandable for people who did not originally write it.</p><p>Over time, these requirements accumulate complexity.</p><p>A small function written for a single task might be easy to understand. The same function, generalized to support five different use cases, can quickly become harder to reason about, harder to test, and harder to modify.</p><p>The more reusable a system becomes, the more constraints it carries.</p><p>Maintenance becomes a balancing act between stability and flexibility. Every change risks breaking someone else&#8217;s use case. Every improvement requires careful coordination.</p><p>In contrast, <strong>problem-specific code has fewer obligations</strong>. It serves one purpose. If that purpose changes, the code can change freely. If the system disappears, the code can disappear with it.</p><p>From a long-term perspective, narrowly scoped solutions can sometimes be <strong>cheaper to maintain than heavily generalized ones</strong>.</p><h2><strong>The Trap of Premature Abstraction</strong></h2><p>Software engineering has long warned against <strong>premature optimization</strong>. A similar problem exists with reuse: <strong>premature abstraction</strong>.</p><p>Generalizing code requires predicting future needs. But predicting future needs is notoriously unreliable.</p><p>Developers often try to design flexible abstractions early in a project, imagining how the system might evolve. These abstractions aim to handle multiple scenarios before those scenarios actually exist.</p><p>The result is often unnecessary complexity.</p><p>Many abstractions end up solving problems that never occur. Others turn out to be slightly wrong once real requirements appear. Instead of simplifying the system, they become obstacles that developers must work around.</p><p>This has led to a growing shift in mindset: <strong>solve the specific problem first</strong>.</p><p>If the same pattern appears multiple times, then abstraction becomes justified. Instead of guessing what might be reusable, developers allow reusable structures to <strong>emerge from repetition</strong>.</p><p>In practice, this often produces simpler systems.</p><h2><strong>Software Is Becoming More Ephemeral</strong></h2><p>Another factor reinforcing this shift is the changing nature of software itself.</p><p>Modern systems are increasingly <strong>ephemeral</strong>.</p><p>Cloud infrastructure spins up and down automatically. Services are deployed and replaced frequently. Feature flags enable experiments that run briefly before being removed. Data pipelines evolve rapidly as requirements change.</p><p>In this environment, many pieces of code exist only temporarily. They support a migration, enable a test, transform data during a transition, or glue together two systems for a limited period of time.</p><p>Treating such code as permanent architecture can be counterproductive. Designing elaborate abstractions for something that will be replaced in six months is often unnecessary.</p><p>Instead, the practical goal becomes <strong>clarity and speed</strong>, not architectural permanence.</p><h2><strong>AI Is Accelerating the Trend</strong></h2><p>The rise of AI-assisted programming tools is amplifying all of these forces.</p><p>When developers can generate working code from descriptions, examples, or prompts, the barrier to producing new software becomes even lower.</p><p>In this environment, the most valuable skill shifts away from writing every line manually and toward <strong>defining problems clearly</strong>.</p><p>Code increasingly becomes a <strong>generated artifact</strong>, produced when needed rather than carefully handcrafted for reuse.</p><p>This does not eliminate the importance of good engineering judgment. But it changes where that judgment is applied. Developers spend less time constructing perfect abstractions and more time deciding <strong>what should exist at all</strong>.</p><p>In other words, the focus moves from code production to <strong>problem framing and system design</strong>.</p><h2><strong>Reuse Has Not Disappeared, It Has Moved</strong></h2><p>Despite these changes, reuse remains essential in software development. It has simply migrated to a different layer.</p><p>Instead of repeatedly reusing internal abstractions within each company, teams now rely heavily on shared infrastructure provided by the broader ecosystem.</p><p>Open-source libraries, cloud services, APIs, and platform frameworks provide the reusable building blocks of modern software.</p><p>Authentication, data storage, messaging, deployment pipelines, monitoring, and machine learning infrastructure are rarely built from scratch anymore. These components are reused at a global scale.</p><p>On top of this shared foundation, however, the application code written by individual teams increasingly becomes <strong>context-specific</strong>.</p><p>It integrates services, encodes business rules, transforms data, and orchestrates workflows. Much of it is unlikely to be reused outside its original context and that is perfectly acceptable.</p><p>The reuse happens below. The customization happens above.</p><h2><strong>From Code Craft to Problem Solving</strong></h2><p>All of this suggests a subtle cultural shift in software engineering.</p><p>For many years, programming culture emphasized <strong>code craftsmanship</strong>. Writing elegant abstractions and reusable architectures was seen as the hallmark of a skilled engineer.</p><p>Those skills are still valuable, particularly in foundational systems. But in many everyday contexts, the highest-value activity is not crafting reusable code it is <strong>solving problems effectively</strong>.</p><p>The goal is not always to build something that lasts forever. Sometimes the goal is simply to deliver a solution that works, is understandable, and can evolve quickly as the situation changes.</p><p>In such environments, code becomes closer to <strong>a tool than a product</strong>.</p><p>Like a script written to automate a task or a spreadsheet created to analyze data, its value lies in the outcome it enables. Once the problem changes, the code can change or disappear.</p><h2><strong>The Scarce Resource Is No Longer Code</strong></h2><p>As the cost of generating software continues to fall, the limiting factor in engineering shifts elsewhere.</p><p>The scarce resource is no longer the ability to write code.</p><p>It is the ability to <strong>understand problems clearly</strong>, choose the right level of abstraction, and decide when complexity is justified. Sometimes the right decision is to design a reusable system. Other times the right decision is to write something simple, solve the immediate issue, and move on.</p><p>Recognizing the difference is becoming one of the most important skills in modern software development. Because in a world where code is cheap, the true value lies not in the code itself but in <strong>knowing why it should exist in the first place</strong></p><p>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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[Code Is Cheap. Correctness Is Not.]]></title><description><![CDATA[Why Tests Matter More Than Code in the Age of AI Coding Agents]]></description><link>https://developertips.substack.com/p/code-is-cheap-correctness-is-not</link><guid isPermaLink="false">https://developertips.substack.com/p/code-is-cheap-correctness-is-not</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Fri, 20 Feb 2026 21:04:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eTob!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Code is becoming disposable.</p><p>Today, you can generate an entire feature in minutes with tools like Claude Code, Codex, Cursor. You describe what you want in plain language, maybe add a few constraints, and an agent produces working code faster than you can finish your coffee.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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>It feels like magic.</p><p>But there&#8217;s a catch.</p><p>The faster we generate code, the less we can trust it.</p><p>And that changes everything.</p><h2><strong>The Bottleneck Has Shifted</strong></h2><p>For decades, writing code was the hard part.</p><p>We spent days or weeks crafting logic, carefully handling edge cases, and reviewing each other&#8217;s work. Tests, when they existed, were often an afterthought written to satisfy coverage thresholds or appease QA.</p><p>Correctness lived mostly in human heads.</p><p>Today, a single developer with a capable AI agent can generate 5&#8211;10&#215; more code than just a couple of years ago. Entire modules, refactors, even backend rewrites can be produced in minutes.</p><p>Velocity is no longer the constraint.</p><p>Trust is.</p><p>When code is cheap and abundant, the real question becomes:</p><p><strong>Does this system actually behave correctly?</strong></p><h2><strong>The Problem With AI-Generated Code</strong></h2><p>AI coding agents are astonishingly good at pattern matching.</p><p>They can:</p><ul><li><p>Scaffold features</p></li><li><p>Translate between frameworks</p></li><li><p>Refactor large codebases</p></li><li><p>Fill in boilerplate instantly</p></li></ul><p>But they are still fundamentally weak at something critical: <strong>understanding truth</strong>.</p><p>They struggle with:</p><ul><li><p>Security boundaries (missing or incorrect authorization checks)</p></li><li><p>Race conditions in distributed systems</p></li><li><p>Performance degradation under load</p></li><li><p>Malicious or unexpected inputs</p></li></ul><p>They produce code that <em>looks right</em> far more often than code that <em>is right</em>.</p><p>And because they&#8217;re fast, they can generate incorrect code at scale.</p><h2><strong>Code Is No Longer the Source of Truth</strong></h2><p>In this new world, something profound has happened:</p><p><strong>The code is no longer the most valuable artifact in your repository.</strong></p><p>It is transient.</p><p>It can be rewritten by an agent tomorrow:</p><ul><li><p>in a different language</p></li><li><p>with a different framework</p></li><li><p>using a new reasoning loop</p></li><li><p>optimized in ways you didn&#8217;t ask for</p></li></ul><p>The implementation is fluid.</p><p>But one thing cannot be fluid:</p><p><strong>What &#8220;correct&#8221; means.</strong></p><p>And there is only one place where that definition can live in an executable, enforceable way:</p><p><strong>Your tests.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eTob!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eTob!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!eTob!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!eTob!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!eTob!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eTob!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15f3cd62-4a1c-46c5-87bc-85e38a01123c_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;:2821160,&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://developertips.substack.com/i/188658750?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.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_!eTob!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!eTob!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!eTob!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!eTob!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15f3cd62-4a1c-46c5-87bc-85e38a01123c_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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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><h2><strong>The Test Suite Is the Constitution</strong></h2><p>Think of your system like a country.</p><p>The implementation is the current government. It can change. It will change.</p><p>The tests are the constitution.</p><p>They encode the invariant of your business, your domain, and your system. If the code violates those rules, it is wrong, no matter how elegant or efficient it looks.</p><p>In 2026, the test suite is no longer a safety net.</p><p><strong>It is the specification.</strong></p><h2><strong>A New Development Workflow</strong></h2><p>This shift changes how we build software. The most effective teams are converging on a new workflow:</p><p><strong>1. Define behavior in tests first</strong></p><p>Translate vague product requirements into executable specifications.</p><p><strong>2. Use the agent to implement</strong></p><p>Provide context and iterate until all tests pass.</p><p><strong>3. Review the diff; but focus on tests</strong></p><p>Implementation can change. Tests define intent.</p><p><strong>4. Merge when the system satisfies the spec</strong></p><p>In this model, you are not primarily writing code. You are defining correctness.</p><h2><strong>What Makes a Good Test in the Agentic Era</strong></h2><p>AI agents can generate basic tests, happy paths, simple assertions, boilerplate.</p><p>But the most valuable tests are still deeply human.</p><p>They require:</p><ul><li><p>domain understanding</p></li><li><p>adversarial thinking</p></li><li><p>experience with failure modes</p></li></ul><p>The tests that matter most are the ones that encode <strong>truth that isn&#8217;t written anywhere else</strong>.</p><p>Examples include:</p><ul><li><p>Business invariant (&#8220;inventory cannot go negative&#8221;)</p></li><li><p>Edge cases (&#8220;empty input with partial state&#8221;)</p></li><li><p>Security constraints (&#8220;user cannot access another account&#8221;)</p></li><li><p>Cross-system consistency (&#8220;ledger matches transactions&#8221;)</p></li><li><p>Property-based guarantees (&#8220;output is always sorted&#8221;)</p></li></ul><h2><strong>Why Weak Tests Fail Faster Now</strong></h2><p>In the pre-AI world, weak testing was survivable. Code changed slowly. Engineers understood most of the system. Bugs were introduced at a human pace.</p><p>Now, change is constant and amplified.</p><p>Agents can:</p><ul><li><p>refactor entire modules</p></li><li><p>swap libraries</p></li><li><p>&#8220;optimize&#8221; logic</p></li><li><p>rewrite APIs</p></li></ul><p>And they will do it confidently. If your tests are shallow, brittle, or overly mocked, they will not protect you. Worse, they can give you a false sense of safety and every refactor becomes a gamble.</p><h2><strong>Practical Guidelines</strong></h2><p>If you&#8217;re working with AI coding agents, a few principles make an outsized difference:</p><ul><li><p><strong>Write tests before implementation</strong><br>Define what success looks like first.</p></li><li><p><strong>Treat tests as specifications, not coverage</strong><br>Coverage numbers don&#8217;t guarantee correctness.</p></li><li><p><strong>Favor integration and behavior tests</strong><br>Mocks break easily under refactors.</p></li><li><p><strong>Use agents to expand, not define, tests</strong><br>Let them fill gaps , but keep control of invariant.</p></li><li><p><strong>Review test changes rigorously</strong><br>A bad test can redefine &#8220;correctness.&#8221;</p></li><li><p><strong>Monitor flakiness like a production issue</strong><br>Unreliable tests destroy trust.</p></li></ul><h2><strong>The New Role of the Engineer</strong></h2><p>For years, great engineers were defined by how well they wrote code. That is no longer the differentiation. Machines can now generate code faster than we can review it.</p><p>But they still don&#8217;t understand:</p><ul><li><p>business rules</p></li><li><p>risk</p></li><li><p>intent</p></li><li><p>truth</p></li></ul><p>That responsibility hasn&#8217;t gone away, it has become more important.</p><p>The best engineers in 2026 are not the ones who prompt the fastest, they are the ones who can precisely define: <strong>what must never break.</strong></p><p>And encode that in tests.</p><h2><strong>A Final Thought</strong></h2><p>For decades, humans were the bottleneck in software correctness. Now machines generate code at incredible speed, but correctness is still on humans.</p><p>Code is cheap.<br>Code is replaceable.</p><p>Correctness is rare.<br>Correctness is priceless.</p><p>And the place we preserve it is in our tests.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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[CLI Is All You Need]]></title><description><![CDATA[The MCP hype is over.]]></description><link>https://developertips.substack.com/p/cli-is-all-you-need</link><guid isPermaLink="false">https://developertips.substack.com/p/cli-is-all-you-need</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Thu, 12 Feb 2026 21:29:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fQYn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What looked like the future of agent tooling is a standardized, structured tool servers for AI agents turned out to be overkill for most real coding work.</p><p>In 2026, the developers shipping the fastest and cleanest are back where they started: the terminal.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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>They give their agents direct shell access and let them use the tools that have existed for decades:</p><pre><code><code>git, rg, grep, npm, docker, curl, jq, tail.</code></code></pre><p>No custom servers.<br>No massive schema descriptions bloating the context window.<br>Just a strong reasoning model + bash/zsh.</p><p>And suddenly, agentic coding actually feels magical.</p><h2><strong>Why MCP lost its shine for everyday development</strong></h2><p>For typical development workflows, MCP often adds friction instead of leverage:</p><ul><li><p><strong>Token overhead:</strong> Verbose tool catalogs and schemas consume precious context.</p></li><li><p><strong>Reinventing the wheel:</strong> Custom MCP servers frequently duplicate what official CLIs already do, more reliably.</p></li><li><p><strong>Poor composability: </strong>You lose the natural piping, chaining, and one-off hacks that Unix perfected decades ago.</p></li><li><p><strong>Model alignment:</strong> Frontier models (Claude, GPT variants, Gemini) were trained extensively on shell usage. They understand flags, pipes, errors, and man-page style docs absurdly well.</p></li></ul><p>The sweet spot is simple:</p><blockquote><p>Drop the agent into your project directory, grant shell execution (with safeguards), and describe the task.</p></blockquote><p>The agent plans, runs commands, edits files, runs tests, commits, debugs, and iterates in a tight loop.</p><p>MCP still has value for highly structured enterprise integrations&#8212;think type-safe SaaS APIs in regulated environments. But for 80&#8211;90% of everyday workflows, it&#8217;s noise.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fQYn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fQYn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fQYn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1174801,&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://developertips.substack.com/i/187792252?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.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_!fQYn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!fQYn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e38780-8696-4c3c-903c-7f80c4044408_1024x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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><h2><strong>The CLI-Native coding agents leading the pack</strong></h2><p><strong>Claude Code (Anthropic)</strong><br>Still the leader for deep reasoning across large or complex codebases. Excellent for architectural discussions, careful refactors, and multi-file changes with thoughtful explanations. Native terminal workflow with file edits, shell access, and git integration. Pay-per-use, but worth it for hard problems.</p><p><strong>Codex CLI (OpenAI)</strong><br>Lightweight, fast, and direct access to powerful OpenAI models. Great for generation, testing, and quick iteration. Open-source roots make it easy to tweak or run locally.</p><p><strong>Gemini CLI (Google)</strong><br>Free tier and strong multimodal capabilities. Excels at rapid prototyping, UI tasks, and large projects. Terminal-first with clean ReAct-style loops. Open-source and privacy-friendly.</p><p><strong>OpenCode</strong><br>Multi-model flexibility (75+ providers), LSP integration, and a strong privacy focus. Many developers call it the most productive terminal agent right now. Rapidly growing community.</p><h2><strong>Where CLI absolutely rushes MCP setups</strong></h2><p><strong>Monorepo-Wide Refactor</strong></p><p>The agent starts with:</p><pre><code><code>rg "oldDeprecatedFunction" .</code></code></pre><p>It plans targeted edits across files, applies changes, reviews with git diff, runs npm test or cargo test, and commits:</p><p>refactor: remove deprecated API calls</p><p>No GitHub MCP server. No bloated context. Just rg, git, and the test runner.</p><p>Full-Stack Debugging on a Production Bug</p><p>Instruction: <em>&#8220;Reproduce the auth failure in staging.&#8221;</em></p><p>The agent runs:</p><pre><code><code>git pull
npm install
npm run dev
tail -f logs/server.log | grep error
curl -v api/auth/check
docker-compose up -d db redis
npm test -- --grep auth</code></code></pre><p>It iterates by editing files, rerunning tests, and probing endpoints.</p><p>No Docker MCP. No logging MCP. Just shell fluency.</p><p><strong>Scaffolding a New Microservice</strong></p><p>Instruction: <em>&#8220;Build a Rust API for user profiles with Postgres using Axum + sqlx.&#8221;</em></p><p>The agent:</p><pre><code><code>cargo new --bin user-service
cargo add axum sqlx --features postgres
cargo watch -x run
curl localhost:3000/health</code></code></pre><p>Commits along the way. No Rust MCP. No database MCP. The existing toolchain is enough.</p><p><strong>CI/CD Flake Fixing</strong></p><p>The agent clones the repo, runs workflows locally with act, spots failures, edits .github/workflows/ci.yml or the Dockerfile, validates with docker build, pushes a branch, and opens a PR with gh.</p><p>All standard CLI tools. No bespoke CI integration layer.</p><h2><strong>A Pattern Developers Keep Reporting</strong></h2><p>Teams that lean into CLI-native agents consistently report:</p><ul><li><p>Higher velocity</p></li><li><p>Fewer token surprises</p></li><li><p>More transparent agent behavior</p></li><li><p>Easier debugging of what the agent is doing</p></li></ul><p>Quotes circulating lately:</p><blockquote><p>&#8220;Stop building integrations. Build CLIs.&#8221;<br>&#8220;Bash is the ultimate MCP.&#8221;<br>&#8220;Agents were trained on Unix pipes&#8212;they&#8217;re ridiculously good at it.&#8221;<br>&#8220;CLI is all you need. Everything else is ceremony.&#8221;</p></blockquote><h2><strong>The Terminal Was Always the Universal Dev Environment</strong></h2><p>In 2026, it&#8217;s also the most powerful interface for AI coding agents.</p><p>Unless you&#8217;re deep inside proprietary enterprise tooling, skip the heavy MCP stack.</p><p>cd into your project.<br>Launch your favorite CLI agent.<br>Give it shell access.<br>Describe the task.</p><p>And watch it work.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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[I see OpenCode more than my family]]></title><description><![CDATA[No, I am not joking.]]></description><link>https://developertips.substack.com/p/i-see-opencode-more-than-my-family</link><guid isPermaLink="false">https://developertips.substack.com/p/i-see-opencode-more-than-my-family</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Fri, 16 Jan 2026 10:15:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!c7IQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I always said &#8216;<em>I use arch and nvim, btw</em>&#8217;, now I have added Opencode to the list.</p><p>A few months ago, I started using OpenCode because I liked the idea of testing different llms quickly without having to change configuration files or anything else.</p><p>Let me first make a quick intro about <a href="https://opencode.ai/">OpenCode</a>. The easiest way to explain what is OpenCode I code it is the description the founders provide:</p><blockquote><p>OpenCode is an open source agent that helps you write code in your terminal, IDE, or desktop.</p></blockquote><p>It is a tool that optimizes the interaction between the developer, the codebase and the llms to provide the best experience in writing code with the AI support.  </p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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>Personally, I really appreciate things that work out of the box, without lengthy configurations or complex interfaces.<br>I started using it every day, initially for fun: &#8216;<em>make an HTML and add a Three.js scene with a sphere</em>&#8217;. &#8216;<em>IT WORKS</em>&#8217;. Funny, but then I went back to my nvim and got my work done!</p><p>Then I remember one day, driven by curiosity and all the hype around me, I tried using it on a real codebase to try to solve an issue. I felt like the first time I wrote my &#8216;Hello World&#8217; in JAVA, yes, my first language was Java.<br>What surprised me most was not the detailed code generation, but the ability to create a plan and the procedural aspect of the agents:</p><p>&#8220;&#8221;&#8220;</p><p>- I am reading the file</p><p>- I am reading its dependency</p><p>- I am looking for further references</p><p>- and so on...</p><p>&#8220;&#8221;&#8220;</p><p>The flow was so reasonable that I immediately felt comfortable using it and trying to explore its limits, its potential, and how I could use it to develop better and learn more concepts at the same time.<br>Another important aspect is the frequency of updates and trying to stay ahead of the competition, as well as supporting new models and their specific features.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c7IQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c7IQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 424w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 848w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 1272w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c7IQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png" width="1158" height="580" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:580,&quot;width&quot;:1158,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22084,&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://developertips.substack.com/i/184752829?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.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_!c7IQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 424w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 848w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 1272w, https://substackcdn.com/image/fetch/$s_!c7IQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F977df000-6d9d-4742-b07d-03d0aa0ce44a_1158x580.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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><strong>Tools, custom configs and agents</strong></h2><p>Any development tool must be customized according to your workflow. There is no one-size-fits-all solution, so be wary of ready-made solutions. As with nvim, which continues to be one of the most widely used editors for writing code, precisely because it allows for extensive customization, the same is true for opencode. It integrates seamlessly without disrupting your workflow, but adapts by offering total customization in terms of creating new tools or agents.</p><p>Over the months I have been using it, I have played around with lots of configurations, and the most useful thing I have found is to customize the configuration for each project, never keeping a fixed one for everything. This is possible precisely because creating new tools or agents is really easy and intuitive.</p><p>Here are some of the ones I use most often, so if you want, you can use them too, or if you have any suggestions, let me know and I&#8217;ll be happy to test new configurations.</p><p>These are three Agents I use frequently:</p><p>1. Code Reviewer Agent (.opencode/agent/review.md)</p><pre><code><code>---
description: Reviews code for best practices, bugs, security, and performance
mode: subagent
model: anthropic/claude-sonnet-4
tools:
  write: false
  edit: false
  bash: false
temperature: 0.1
permissions:
  edit: deny
  bash: deny
  webfetch: allow
---
You are a code reviewer. Analyze provided code for:
- Code quality and best practices (e.g., naming, structure).
- Potential bugs and edge cases.
- Security vulnerabilities (e.g., injection, auth flaws).
- Performance implications (e.g., inefficiencies).
- Maintainability and readability.
Provide constructive feedback without making changes. Use examples from the codebase if referenced.</code></code></pre><p>The <strong>Code Reviewer Agent</strong> focuses on improving overall code quality by examining structure, naming, readability, performance, and potential bugs or edge cases. It provides constructive feedback and best-practice recommendations without modifying the code.</p><p>2. Security Auditor Agent (.opencode/agent/security.md)</p><pre><code><code>---
description: Performs security audits and identifies vulnerabilities
mode: subagent
model: anthropic/claude-sonnet-4
tools:
  write: false
  edit: false
  bash: false
temperature: 0.1
permissions:
  edit: deny
  bash: deny
  webfetch: allow
---
You are a security expert. Scan code for:
- Input validation vulnerabilities.
- Authentication/authorization flaws.
- Data exposure risks.
- Dependency vulnerabilities (e.g., outdated packages).
- Configuration security issues.
Flag high-risk items with mitigation suggestions. Reference OWASP guidelines if relevant.</code></code></pre><p>The <strong>Security Auditor Agent</strong> specializes in identifying security risks within the codebase, such as input validation flaws, authentication or authorization weaknesses, data exposure, and insecure configurations. It flags high-risk vulnerabilities and suggests mitigations.</p><p>3. Documentation Writer Agent (.opencode/agent/docs.md)</p><pre><code><code>---
description: Writes and maintains project documentation
mode: subagent
model: anthropic/claude-haiku-4
tools:
  write: true
  edit: true
  bash: false
temperature: 0.3
permissions:
  edit: ask
  bash: deny
  webfetch: allow
---
You are a technical writer. Create clear, comprehensive documentation.
Focus on:
- Clear explanations with examples.
- Proper structure (e.g., headings, sections).
- User-friendly language.
- Code snippets and references.
Follow formats like: Title as H1, sections with H2/H3, concise bullet points.</code></code></pre><p>The <strong>Documentation Writer Agent</strong> is responsible for creating and maintaining clear, well-structured technical documentation. It explains features and workflows in accessible language, includes examples and code snippets where helpful, and ensures documentation is organized and easy to navigate for developers and users alike.</p><p>For each agent I try to use the proper llm, finding the best one in terms of costs/performances. For example for documentation agent I use claude-haiku, which is less accurate than sonnet but it is also cheaper. Testing it a little bit gives me good vibes and the produced documentation is good enough that convinced me to choose it.  </p><p>An example on how to use them (I add a space between @ word, to not tag random accounts here) :</p><ul><li><p>@ review this feature/ &#8594; fixes &amp; suggestions </p></li><li><p>@ security check auth/ &#8594; vuln report </p></li><li><p>@ docs write README for api/ &#8594; <code>detailed docs</code> </p><p></p></li></ul><p>If agents are pretty intuitive and they are a list of instructions, tools are more programming stuff. A tool is a pre-defined structure that the agent (or better the llm) can use to make some actions. They are especially useful if you have some repetitive actions in your projects, for example testing the REST API service you are developing.</p><p>The following one is a simple api-test tool that I use daily. As a backend developer I have always to test the messages, response codes, routes consistency of my APIs. There are many tools for doing that like classical CLI command: curl, wget, httpie; or more advanced GUI tools like Insomnia or Postman. </p><p>API Tester Tool (.opencode/tool/api-test.ts)</p><pre><code><code>import { tool } from "@opencode-ai/plugin";
export default tool({
  description: "Test API endpoints with HTTP requests",
  args: {
    url: tool.schema.string().url().describe("API endpoint URL"),
    method: tool.schema.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method"),
    body: tool.schema.string().optional().describe("Request body (JSON string)"),
  },
  async execute(args) {
    const response = await fetch(args.url, {
      method: args.method,
      headers: { "Content-Type": "application/json" },
      body: args.body ? JSON.stringify(JSON.parse(args.body)) : undefined,
    });
    const data = await response.text();
    return `Status: ${response.status}\nResponse: ${data}`;
  },
});</code></code></pre><p>Here I leave a few bonus tools: </p><p>1. Code Formatter Tool (opencode/tool/format.ts)</p><pre><code><code>import { tool } from "@opencode-ai/plugin";
export default tool({
  description: "Format code files using Prettier or similar",
  args: {
    filePath: tool.schema.string().describe("Path to file to format"),
    formatter: tool.schema.enum(["prettier", "black"]).default("prettier").describe("Formatter to use"),
  },
  async execute(args) {
    // Use bash to invoke formatter (assumes installed)
    const cmd = args.formatter === "prettier" ? `prettier --write ${args.filePath}` : `black ${args.filePath}`;
    await Bun.$`${cmd}`; // Or use bash tool if needed
    return `Formatted ${args.filePath} with ${args.formatter}`;
  },
});</code></code></pre><p>2. Log Analyzer Tool (.opencode/tool/log-analyze.ts)</p><pre><code><code>import { tool } from "@opencode-ai/plugin";
export default tool({
  description: "Analyze log files for errors and summaries",
  args: {
    logPath: tool.schema.string().describe("Path to log file"),
    pattern: tool.schema.string().default("ERROR").describe("Regex pattern to search for"),
  },
  async execute(args) {
    // Read file and grep (or use built-in grep tool)
    const content = await Bun.file(args.logPath).text();
    const matches = content.match(new RegExp(args.pattern, "g")) || [];
    return `Found ${matches.length} matches for "${args.pattern}" in ${args.logPath}. Sample: ${matches.slice(0, 5).join(", ")}`;
  },
});</code></code></pre><p>Once you have defined a tool you have nothing to do, just let the agent use it when necessary. Let me do an example:</p><p>PROMPT: &#8220;add a new GET route /api/v2/alive, the status code for success is 200 and the return message is {&#8220;Version&#8221;: {get_version_from_env()}}. Once implemented, before writing the test, quickly test with the api testing tool.&#8221;</p><p>This prompt instruct the agent to create the new route, use the tool to test if it works and then add the test to the /tests folder. As you can see I don&#8217;t invoke explicitly the tool, just letting know the llm that you should have a look on the available tools because there is probably something useful for this task.</p><p>That&#8217;s it. I&#8217;ll try to improve every day my workflow and my journey on agentic ai. I hope you have found this article interesting and let me know which is your preferred workflow. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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[Computer Vision for Edge Devices: Seeing Like a Machine]]></title><description><![CDATA[Computer vision often feels like magic. In this series we will try to understand the magic and become cv wizards.]]></description><link>https://developertips.substack.com/p/computer-vision-for-edge-devices</link><guid isPermaLink="false">https://developertips.substack.com/p/computer-vision-for-edge-devices</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Fri, 09 Jan 2026 23:35:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/082a92dc-bd2c-403f-965f-8aaaf217bc1b_640x406.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your phone unlocks itself by looking at your face. A small camera can detect people, cars, defects on a production line. Tiny devices seem to <em>understand</em> the world through images.</p><p>But before talking about neural networks, models, or edge accelerators, we need to step back.</p><p>To deploy computer vision on small hardware, we must first understand a simple truth:</p><blockquote><p>Machines do not see images. They process data.</p></blockquote><p>This first article lays the foundation for the series. We will start from the very basics: what an image really is, how machines extract information from it, and why this matters when your final target is a constrained edge device.</p><h2><strong>What does it mean for a machine to &#8220;see&#8221;?</strong></h2><p>When humans look at an image, we instantly recognize meaning: faces, objects, motion, intent. This happens so naturally that we forget how complex the process actually is.</p><p>A machine, however, does not see <em>objects</em>. It does not see <em>faces</em>. It does not even see <em>shapes</em>.</p><p>A machine sees <strong>numbers</strong>.</p><p>Computer vision is the discipline of turning raw numerical data into useful information. Everything else detection, classification, tracking is built on top of that transformation.</p><p>Understanding this point is essential, especially for edge devices, where every computation has a cost.</p><h2><strong>An image is just structured data</strong></h2><p>At its core, an image is a grid.</p><p>Each cell in that grid is called a <strong>pixel</strong>, and each pixel contains one or more numbers describing intensity.</p><p>A grayscale image can be imagined as a two-dimensional table:</p><p>python</p><pre><code><code>image = [
  [12, 15, 18, ...],
  [10, 14, 20, ...],
  [...]
]</code></code></pre><p>Each number represents how bright that pixel is.</p><p>Color images are just an extension of the same idea. Instead of one grid, you have multiple grids stacked together typically one for red, one for green, and one for blue.</p><p>So an image is not a picture. It is a <strong>data structure with spatial meaning</strong>.</p><p>This perspective is crucial for edge computing: memory usage, data movement, and numerical precision all depend on how these grids are stored and processed.</p><h2><strong>Resolution and precision</strong></h2><p>Two properties of images matter a lot when working on small hardware:</p><ul><li><p><strong>Resolution</strong> (how many pixels)</p></li><li><p><strong>Precision</strong> (how many bits per pixel)</p></li></ul><p>Higher resolution means more data. Higher precision means more memory and more computation.</p><p>On powerful servers, this often goes unnoticed. On edge devices, it is the difference between a system that runs smoothly and one that fails to deploy.</p><p>One of the recurring themes in this series will be learning how to ask:</p><blockquote><p>&#8220;Do I really need this much information?&#8221;</p></blockquote><p>Because in computer vision, more data is not always better.</p><h2><strong>Locality: vision emerges from neighborhoods</strong></h2><p>One of the most important ideas in computer vision is <strong>locality</strong>.</p><p>Most useful visual information comes from comparing a pixel with its neighbors.</p><p>Edges appear where intensity changes abruptly. Textures emerge from repeating local patterns. Motion is detected by observing how local regions change over time.</p><p>Conceptually, many vision algorithms follow this pattern:</p><p>bash</p><pre><code><code>for each pixel:
  compare with nearby pixels</code></code></pre><p>This simple idea explains why computer vision scales well and why it can be optimized for edge devices.</p><p>Local operations can often be computed efficiently, reused, or approximated. They also map naturally to specialized hardware and low-power processing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CEVY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CEVY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 424w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 848w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 1272w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CEVY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png" width="893" height="772" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:772,&quot;width&quot;:893,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:467415,&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://developertips.substack.com/i/184078309?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.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_!CEVY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 424w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 848w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 1272w, https://substackcdn.com/image/fetch/$s_!CEVY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48675f76-d0dd-4551-8b88-f1461f80878d_893x772.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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>Computer vision application. How a computer sees an image. </p><h2><strong>Computer vision as transformation, not intelligence</strong></h2><p>It is tempting to think of computer vision as a form of artificial intelligence that <em>understands</em> images.</p><p>In practice, it is better described as a <strong>pipeline of transformations</strong>.</p><p>A typical vision system looks like this:</p><ol><li><p>Capture an image from a sensor</p></li><li><p>Preprocess it (resize, normalize, filter)</p></li><li><p>Extract relevant features or representations</p></li><li><p>Interpret those representations</p></li><li><p>Produce an output or decision</p></li></ol><p>Each step transforms data into a more useful form.</p><p>For edge devices, this pipeline mindset is essential. It allows you to:</p><ul><li><p>Move computation closer to the sensor</p></li><li><p>Remove unnecessary steps</p></li><li><p>Decide what must run locally and what can run elsewhere</p></li></ul><p>Efficiency comes from understanding <em>where</em> value is created along the pipeline.</p><h2><strong>Before deep learning, vision already worked</strong></h2><p>Modern computer vision is often associated exclusively with deep learning.</p><p>But long before neural networks became popular, systems were already detecting edges, tracking motion, recognizing shapes, and measuring distances often in real time and on very limited hardware.</p><p>Why does this matter today?</p><p>Because edge devices force us to revisit the same constraints:</p><ul><li><p>Limited memory</p></li><li><p>Limited power</p></li><li><p>Real-time requirements</p></li></ul><p>Classic computer vision techniques are not obsolete. In many edge scenarios, they are still the most efficient solution or an essential pre-processing step before a learned model.</p><h2><strong>Why edge devices change the rules</strong></h2><p>Running computer vision on the edge is not just about moving models closer to the camera.</p><p>It introduces new priorities:</p><ul><li><p><strong>Latency</strong>: decisions must be immediate</p></li><li><p><strong>Power</strong>: every operation consumes energy</p></li><li><p><strong>Memory</strong>: models must fit in constrained environments</p></li><li><p><strong>Reliability</strong>: connectivity may be unavailable</p></li><li><p><strong>Privacy</strong>: raw images often cannot leave the device</p></li></ul><p>These constraints force us to think carefully about <em>what</em> we compute and <em>why</em>.</p><p>Understanding images as data, and vision as transformation, is the first step toward making the right trade-offs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dMlZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dMlZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dMlZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg" width="960" height="1280" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1280,&quot;width&quot;:960,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:166682,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://developertips.substack.com/i/184078309?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dMlZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dMlZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd17275e7-ffa3-41b9-bc95-254f4138c73f_960x1280.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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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>ESP32 stream video application example</p><h2><strong>Looking ahead</strong></h2><p>In this first article, we stripped computer vision down to its essentials:</p><ul><li><p>Images are structured numerical data</p></li><li><p>Visual information emerges from local patterns</p></li><li><p>Vision systems are pipelines, not black boxes</p></li></ul><p>In the next episode, we will explore how these basic ideas evolved into modern computer vision approaches and how deep learning fits into the picture, especially when your final target is a small, resource-constrained device.</p><p>Computer vision on the edge is not about making machines <em>see like humans</em>.</p><p>It is about making them see <strong>just enough</strong>.</p><p>Code for the computer visions projects:</p><p><a href="https://github.com/orgs/computer-vision-with-marco/repositories">https://github.com/orgs/computer-vision-with-marco/repositories</a></p>]]></content:encoded></item><item><title><![CDATA[How to Create and Manage a Service in an Alpine Linux Container]]></title><description><![CDATA[Because sometimes you need more than an entrypoint.]]></description><link>https://developertips.substack.com/p/how-to-create-and-manage-a-service</link><guid isPermaLink="false">https://developertips.substack.com/p/how-to-create-and-manage-a-service</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Fri, 06 Sep 2024 14:17:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6640d165-b93c-46ee-822e-dfd2aabe1215_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Alpine Linux is a lightweight and security-focused Linux distribution that's widely used in Docker containers. With its small footprint, it's ideal for building minimalistic containers, but it may not come with all the tools you're used to on other distributions. For example, services are managed using <strong>OpenRC</strong> instead of the more commonly known <strong>systemd</strong>.</p><p>In this article, I'll walk you through the process of setting up and managing a custom service inside an Alpine Linux container, using OpenRC. This could be very useful when you need to run some service alongside your main entrypoint service.</p><p></p><h3>Prerequisites</h3><p>Before starting, make sure you have the following:</p><ul><li><p>A running Alpine Linux container.</p></li><li><p>Basic knowledge of Linux and OpenRC.</p></li><li><p>Access to install packages within the container.</p></li></ul><p>If you're not familiar with OpenRC, it&#8217;s a simple and efficient init system, perfect for resource-constrained environments like Alpine.</p><p></p><h3>Step 1: Installing OpenRC (If Necessary)</h3><p>First, check if OpenRC is installed in your Alpine container. Many base images come with it pre-installed, but if you&#8217;re using a very minimal image, it may be missing. To install OpenRC, run:</p><pre><code><code>apk add openrc</code></code></pre><p>Once installed, OpenRC will provide you with tools to manage system services.</p><p></p><h3>Step 2: Writing the Service Script</h3><p>Let's say we want to create a service that monitors a directory for file creation and the new files name automatically. We'll write a script for this service and place it in<code> /usr/local/bin</code>.</p><p>Here's an example of a monitoring script using inotifywait, which you can install with:</p><pre><code>apk add inotify-tools</code></pre><p>Create the script:</p><pre><code>vi /usr/local/bin/monitor_directory.sh</code></pre><p>Add some content like:</p><pre><code>#!/bin/sh
DIRECTORY_TO_WATCH="/path/to/directory"
echo $$ &gt; /run/monitor_directory.pid  # Store the PID of this process
inotifywait -m -e create --format '%w%f' "$DIRECTORY_TO_WATCH" | while read NEW_FILE
do
    echo $NEW_FILE"
done</code></pre><p>Make the script executable:</p><pre><code>chmod +x /usr/local/bin/monitor_directory.sh</code></pre><p>This script monitors the directory for file creation events and print the name of the newly created files.</p><p></p><h3>Step 3: Creating the OpenRC Service</h3><p>Now, let's create an OpenRC service that manages this script. Services in OpenRC are managed through init scripts located in <code>/etc/init.d.</code> We'll create one for our file monitoring service.<br>If you don&#8217;t find the /etc/init.d folder, than you need to install the util-linux package:</p><pre><code>apk add util-linux</code></pre><p>Create the service file:</p><pre><code>vi /etc/init.d/monitor_directory</code></pre><p>Paste the following content:</p><pre><code>#!/sbin/openrc-run

name="Monitor Directory"
description="Monitor a directory"
command="/usr/local/bin/monitor_directory.sh"
command_background=true
pidfile="/run/monitor_directory.pid"</code></pre><p>Make the service file executable:</p><pre><code>chmod +x /etc/init.d/monitor_directory</code></pre><p></p><h3>Step 4: Using <code>command_background=true</code> and <code>pidfile</code></h3><p>In the service script, we set command_background=true, which tells OpenRC to run the script in the background. When running a process in the background, it's important to also specify a <code>pidfile</code>. This is a file where the process writes its PID (process ID), allowing OpenRC to track the running process.</p><p>In our case, the <code>pidfile</code> is located at /run/monitor_directory.pid, and the script writes its own PID to this file using the following line:</p><pre><code>echo $$ &gt; /run/monitor_directory.pid</code></pre><p>The combination of <code>command_background=true</code> and <code>pidfile</code> ensures that OpenRC can correctly manage the background service, allowing you to start, stop, and restart it.</p><h3><br><br>Step 5: Enabling and Starting the Service</h3><p>Once the service script is ready, we need to enable it to start at boot and start it immediately. To be sure that the container will run at the desired runlevel (which is <code>debug) we can execute the following command:</code></p><pre><code><code>openrc default</code></code></pre><p>To add the service to the default runlevel so it starts on boot:</p><pre><code>rc-update add monitor_directory default</code></pre><p>To start the service manually without rebooting:</p><pre><code>service monitor_directory start</code></pre><p></p><h3>Step 6: Dockerfile and Entrypoint for Your Alpine Service</h3><p>To set up your Alpine Linux container with the custom service you created, you'll need to define a <code>Dockerfile</code> and an entrypoint script. This will ensure that the service starts automatically when the container is launched.</p><p>Here&#8217;s how you can structure the <code>Dockerfile</code> and the entrypoint script.</p><p><strong>Dockerfile</strong></p><pre><code>FROM alpine:latest

RUN apk update &amp;&amp; \
    apk add openrc inotify-tools &amp;&amp; \
    mkdir -p /run/openrc &amp;&amp; \
    touch /run/openrc/softlevel

COPY monitor_directory.sh /usr/local/bin/monitor_directory.sh

COPY monitor_directory_service /etc/init.d/monitor_directory

RUN chmod +x /usr/local/bin/monitor_directory.sh &amp;&amp; \
    chmod +x /etc/init.d/monitor_directory

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]</code></pre><p><strong>entrypoint.sh</strong></p><pre><code>#!/bin/sh

openrc default

rc-update add monitor_directory default

rc-service monitor_directory start

exec "$@"</code></pre><p></p><h3>Conclusion</h3><p>By configuring the <code>Dockerfile</code> and the entrypoint correctly, you can build a Docker container that not only runs your custom service inside Alpine Linux but also ensures that the service starts automatically when the container is launched. This makes it easy to manage services in a lightweight container environment.</p><p>Alpine Linux's minimalism and OpenRC's simplicity make this a perfect combination for containerized environments, where efficiency and control are key.</p><h3><br><br>Troubleshooting</h3><ol><li><p><strong>Service Fails to Start:</strong><br>Check the <code>/var/log/messages</code> or <code>/var/log/rc.log</code> files for any errors.</p></li><li><p><strong>PID File Not Found:</strong><br>Make sure your script writes the PID to the <code>pidfile</code> location specified in the service script. You can check this by manually starting the script and inspecting the contents of <code>/run/monitor_directory.pid</code>.</p></li><li><p><strong>Services Not Persisting Across Reboots:</strong><br>If your service isn't starting automatically after a reboot, verify that it&#8217;s added to the <code>default</code> runlevel with <code>rc-update</code>.</p></li></ol><p>By following these steps, you should be able to set up any service in an Alpine Linux container and manage it effectively. Happy hacking!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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[Unlocking the Power of GPUs in Docker: A Comprehensive Guide]]></title><description><![CDATA[Docker is the most used engine to run our containers, but what happens if our application requires a GPU ? Which the fastest way to have a full GPU&#8217;s compatible environment ? Let&#8217;s see!]]></description><link>https://developertips.substack.com/p/unlocking-the-power-of-gpus-in-docker</link><guid isPermaLink="false">https://developertips.substack.com/p/unlocking-the-power-of-gpus-in-docker</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Thu, 19 Oct 2023 08:15:30 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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://developertips.substack.com/subscribe?"><span>Subscribe now</span></a></p><h2>Why configure Docker for using GPUs will be more and more important ?</h2><p>Docker is the &#8216;de facto&#8217; tool to create, test and deploy in production the largest part of the current software and web application. Due to the exponential improvement of Machine and Deep Learning technologies, GPUs have a central role in the development phase, but also in the deployment.</p><p>For sure, during the design and training of the model, a large number of GPUs is required to parallelize and speed up this initial phase. However, in the production phase, it is fundamental guarantee the maximum of speed, during the inference step. This means that, a correct configuration of Docker with GPUs could be beneficial for initial phases, but even more for your real business.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" width="4032" height="3024" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3024,&quot;width&quot;:4032,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a close up of a graphics card on a wooden surface&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&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="a close up of a graphics card on a wooden surface" title="a close up of a graphics card on a wooden surface" srcset="https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1674741659137-b82a0b687ded?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8Z3B1fGVufDB8fHx8MTY5NzcwMzEyNnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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">Photo by <a href="https://unsplash.com/@zelebb">Andrey Matveev</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><h2>First step: instance configuration</h2><p>To run a container with a GPU requirement, you had to be sure to have installed the NVIDIA Container Toolkit.<br>Setup the package repository and the GPG key:</p><pre><code>distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&amp;&amp; curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&amp;&amp; curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list</code></pre><p>Install the <strong>nvidia-docker2</strong> package (and dependencies) after updating the package listing:</p><pre><code>sudo apt update &amp;&amp; sudo apt install -y nvidia-docker2</code></pre><p>Restart Docker to apply changes:</p><pre><code>sudo systemctl restart docker</code></pre><p>Now, to be sure that all works fine, test with a minimal image:</p><pre><code>sudo docker run --rm --gpus all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi</code></pre><p>This should return the output of the <code>nvidia-smi</code> command.<br>For more information, <a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html">HERE</a> the official documentation.</p><h2>Second step: learn the most important configuration options</h2><p>In the docker run command above, we use the <code>--gpu</code> option, passing <code>all</code> as argument. This means that docker can use all the GPUs available. Sometimes you don&#8217;t want to use all the GPUs, for example for an unbalance of configurations. Mixing different GPUs, for example with 6 GB VRAM and 12 GB VRAM could lead some unexpected behavior. You can be more specific using different arguments like:</p><ul><li><p><code>device=&lt;GPU-ID&gt;</code> force to use exactly this GPU</p></li><li><p><code>"device=0,2"</code> force to use just the GPU number 0 and 2, excluding the 1.</p></li><li><p><code>'all,capabilities=utility'</code> gives a docker access to all the GPUs available, than<br>set as ENV variable <code>capabilities=utility</code>, which add to the container some monitoring tools.</p></li></ul><h2>Third step: deploy a real machine learning project</h2><p>Test our configuration with a real application, using Tensorflow Serving. It is a quick way to wrap your model in a web-server, ready to be used in production.<br>We want to expose an API REST endpoint on the 8501 port. The Tensorflow serving image requires a mounted volume that contains my model, into <code>/model</code> directory.</p><pre><code>docker run --gpus all -p 8501:8501 \
--mount type=bind,\
source=/path/to/my_model/,target=/models/my_model \
-e MODEL_NAME=my_model -t tensorflow/serving:latest-gpu</code></pre><p>That&#8217;s all folks! Hope that this quick walk-through will be useful for your next machine learning projects!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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 Ultimate VS Code Setup for Python Developers: Tips, Tricks, and Essential Extensions]]></title><description><![CDATA[As a Python developer, your choice of Integrated Development Environment (IDE) can make a huge difference in your productivity, code quality, and overall programming experience.]]></description><link>https://developertips.substack.com/p/the-ultimate-vs-code-setup-for-python</link><guid isPermaLink="false">https://developertips.substack.com/p/the-ultimate-vs-code-setup-for-python</guid><dc:creator><![CDATA[Marco Franzon]]></dc:creator><pubDate>Fri, 13 Oct 2023 10:40:09 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" width="3353" height="2514" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2514,&quot;width&quot;:3353,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;closeup photo of eyeglasses&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&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="closeup photo of eyeglasses" title="closeup photo of eyeglasses" srcset="https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1504639725590-34d0984388bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMnx8Y29kaW5nfGVufDB8fHx8MTY5NzE5MzUyNXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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">Photo by <a href="https://unsplash.com/@ikukevk">Kevin Ku</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>In this article, we&#8217;ll explore some of the best VS Code setups and configurations for Python development, including essential extensions, customization, and tips that can help you take your Python coding to the next level.</p><h2>Extensions</h2><ul><li><p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python">Python</a></strong>: with the Python Extension, you can take advantage of features like code highlighting, IntelliSense, code navigation, and code formatting. Additionally, it includes built-in support for popular Python frameworks like Django, Flask, and Pyramid. The extension also provides an interactive Python interpreter, enabling you to experiment with code snippets and perform quick calculations without leaving the editor. With integrated debugging support, you can quickly identify and fix any issues in your Python code.</p></li><li><p><strong><a href="https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent">Python Indent</a></strong>: the Python Indent extension helps to automate the process of indentation, adjusting the code blocks as you type. With this extension installed, you can focus on writing your code, and the extension will take care of the indentation for you. This can save a considerable amount of time and reduce errors caused by incorrect indentation. The Python Indent extension also supports multiple indentation styles, including tabs, spaces, and mixed, allowing you to customize the indentation style to match your coding preferences.</p></li><li><p><strong><a href="https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring">autoDocstring &#8212; Python Docstring Generator</a></strong>: Docstrings are essential elements of Python code, as they provide documentation for functions and methods, explaining what they do and how they work. However, writing docstrings can be a time-consuming and tedious process, especially when you have to write them for numerous functions and methods. The autoDocstring extension helps to automate this process, generating docstrings automatically based on the function or method signature. With just a few keystrokes, you can generate a complete docstring that includes information on the function&#8217;s parameters, return values, and any exceptions that may be raised. The autoDocstring extension also supports customization, allowing you to define your docstring format and style.</p></li></ul><h2>Configurations</h2><p>Once you have installed the previous extensions, it is time to edit the settings of Visual Studio Code. This IDE has two settings file, the <code>defaultSettings.json</code> which has the default configuration of the IDE and the <code>settings.json</code> which is the user configuration. The latter is the one that we are going to modify with custom parameters.</p><p>First of all let&#8217;s see how to find this file. You can directly navigate to the <code>settings.json</code> file on your computer by following these steps:</p><ol><li><p>Open Visual Studio Code.</p></li><li><p>Open the Command Palette by pressing &#8220;Ctrl + Shift + P&#8221; (Windows and Linux) or &#8220;Cmd + Shift + P&#8221; (macOS).</p></li><li><p>In the Command Palette, type &#8220;Open User Settings&#8221; and select &#8220;Preferences: Open User Settings&#8221; from the list.</p></li><li><p>In the Settings editor, click on the &#8220;Open Settings (JSON)&#8221; link at the top right corner of the window.</p></li></ol><p>This will open the <code>settings.json</code> file in the editor, where you can edit the settings for Visual Studio Code. The first time it looks like very minimal with just few parameters like this:</p><pre><code>{
    "workbench.colorTheme": "SynthWave '84",
    "window.zoomLevel": 2,
}</code></pre><p>In my case the <code>colorTheme</code>(I love <a href="https://marketplace.visualstudio.com/items?itemName=RobbOwen.synthwave-vscode">SynthWave &#8217;84</a>) and the <code>zoomLevel</code>of the IDE window. These are general settings, not related to a language, to define some specific parameters just on a specific file format you need something like this:</p><pre><code>{
    "workbench.colorTheme": "SynthWave '84",
    "window.zoomLevel": 2,
    "[python]": { // python section  
        "editor.rulers": [88],
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        },
        "editor.formatOnType": true
    },
}</code></pre><p>Introducing the <code>"[python]"</code> key you can add a configuration, which will be applied just when you are editing a <code>.py</code>file. As you can see these are editor&#8217;s configurations, like <code>editor.rulers</code> which insert a vertical line after 88 characters.</p><p>I highly suggest to have a section for each language, in this way you have always the best experience without any overlapping setting.</p><p>Let&#8217;s move on the section regarding python&#8217;s extensions settings.</p><pre><code>{
    "workbench.colorTheme": "SynthWave '84",
    "window.zoomLevel": 2,
    "[python]": { // python section  
        "editor.rulers": [88],
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        },
        "editor.formatOnType": true
      },
    // Local interpreter can be the default python 
    // or if you are using poetry "$(poetry run which python)"
    "python.defaultInterpreterPath": "/usr/local/bin/python3",
    "python.linting.enabled": true,
    "python.formatting.provider": "black",
    "python.sortImports.args": ["--profile", "black"],
    "python.analysis.typeCheckingMode": "strict",
    "python.analysis.autoSearchPaths": true,
    "python.languageServer": "Default",
    "python.linting.pylintEnabled": true,
    "python.linting.flake8Enabled": true,
    "python.linting.flake8Args": [
        // Match what black does.
        "--max-line-length=88"
    ],
}</code></pre><p>The above <code>settings.json</code> file contains Python-related settings for Visual Studio Code. Here is a breakdown of each setting:</p><ul><li><p><code>python.defaultInterpreterPath</code>: Specifies the path to the default Python interpreter. In this case, it is set to <code>/usr/local/bin/python3</code>.</p></li><li><p><code>python.linting.enabled</code>: Determines whether linting is enabled for Python code. It is set to <code>true</code> here, meaning that linting is disabled.</p></li><li><p><code>python.formatting.provider</code>: Specifies the formatting provider to use for Python code. Here, it is set to use the <code>black</code> formatter.</p></li><li><p><code>python.sortImports.args</code>: Specifies the arguments to use when running the <code>isort</code> utility for sorting imports in Python code. Here, it is set to <code>--profile black</code>, meaning that the <code>black</code> profile is used for sorting.</p></li><li><p><code>python.analysis.typeCheckingMode</code>: Specifies the type checking mode to use for Python code. Here, it is set to <code>strict</code>.</p></li><li><p><code>python.analysis.autoSearchPaths</code>: Determines whether Visual Studio Code should automatically search for Python packages installed in the project's environment. It is set to <code>true</code> here.</p></li><li><p><code>python.languageServer</code>: Specifies the language server to use for Python code. Here, it is set to <code>Default</code>, meaning that the default language server is used, alternative could be <code>Pylance</code></p></li><li><p><code>python.linting.pylintEnabled</code>: Determines whether <code>pylint</code> is enabled for linting Python code. It is set to <code>false</code> here.</p></li><li><p><code>python.linting.flake8Enabled</code>: Determines whether <code>flake8</code> is enabled for linting Python code. It is set to <code>true</code> here.</p></li><li><p><code>python.linting.flake8Args</code>: Specifies the arguments to use when running <code>flake8</code>. Here, it is set to use a <code>--max-line-length</code> argument with a value of <code>88</code>, which matches what the <code>black</code> formatter does.</p><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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://developertips.substack.com/subscribe?"><span>Subscribe now</span></a></p></li></ul><h2>Command Shortcuts</h2><p>Bonus section regarding the command shortcuts, which is not strictly related to python but a recap is always useful.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1xG-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1xG-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 424w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 848w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 1272w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1xG-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png" width="1167" height="865" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:865,&quot;width&quot;:1167,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52648,&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_!1xG-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 424w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 848w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 1272w, https://substackcdn.com/image/fetch/$s_!1xG-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe31310ac-5469-49a7-9efe-0bec912e7872_1167x865.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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 role="img" width="20" height="20" viewBox="0 0 20 20" 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><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.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>Note: Some shortcuts may differ depending on your operating system and keyboard layout.</p><p>That&#8217;s a wrap! Thanks for reading, I hope you will find it interesting a useful. Let me know if you have suggestions for other useful configurations to consider!</p><div><hr></div><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://developertips.substack.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 Developer Tips! 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></channel></rss>