<?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[*codeplain]]></title><description><![CDATA[spec-driven, production-ready code generation]]></description><link>https://blog.codeplain.ai</link><image><url>https://substackcdn.com/image/fetch/$s_!11KS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6e6d31-2c91-4469-bb07-a7ac182a349b_1280x1280.png</url><title>*codeplain</title><link>https://blog.codeplain.ai</link></image><generator>Substack</generator><lastBuildDate>Tue, 21 Apr 2026 19:55:19 GMT</lastBuildDate><atom:link href="https://blog.codeplain.ai/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[*codeplain]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[info@codeplain.ai]]></webMaster><itunes:owner><itunes:email><![CDATA[info@codeplain.ai]]></itunes:email><itunes:name><![CDATA[Codeplain]]></itunes:name></itunes:owner><itunes:author><![CDATA[Codeplain]]></itunes:author><googleplay:owner><![CDATA[info@codeplain.ai]]></googleplay:owner><googleplay:email><![CDATA[info@codeplain.ai]]></googleplay:email><googleplay:author><![CDATA[Codeplain]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Predrag Radenkovic: Scaling integrations at Incode using spec-driven development | OpenTalks]]></title><description><![CDATA[Watch now | In this talk at OpenTalks, *codeplain CTO Predrag Radenkovic presents how Incode, a $3B identity verification platform, transformed its integration development using spec-driven development, while direct LLM-based code generation with tools like Claude Code failed to scale.]]></description><link>https://blog.codeplain.ai/p/predrag-radenkovic-scaling-integrations</link><guid isPermaLink="false">https://blog.codeplain.ai/p/predrag-radenkovic-scaling-integrations</guid><dc:creator><![CDATA[Codeplain]]></dc:creator><pubDate>Tue, 14 Apr 2026 07:45:52 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/193696108/c089352ae63f911d7b6c6ddb78ae69a9.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>In this talk at <a href="https://opentalks.ai/">OpenTalks</a>, <a href="https://www.codeplain.ai/">*codeplain</a> CTO <a href="https://www.linkedin.com/in/pedja-radenkovic/">Predrag Radenkovic</a> presents how <a href="https://www.incode.com/">Incode</a>, a $3B identity verification platform, transformed its integration development using spec-driven development, while direct LLM-based code generation with tools like Claude Code failed to scale.<br><br>Initial attempts at automation produced code that diverged from requirements, introduced bugs, and increased review burden. The agentic approach, lacking strict guidance, led to unreliable outputs. The breakthrough came with spec-driven development, where structured specifications become the single source of truth.<br><br>Using ***plain, the language of spec-driven development, Incode translates requirements directly into working software, reuses modular components across integrations, and automatically detects ambiguities and conflicts before code generation. *codeplain's fully automated rendering pipeline&#8212;combining structured specs, a state-machine agent, LLMs, and continuous test validation&#8212;renders, tests, and validates each functional requirement independently and as a whole. This enables regression safety, precise bug fixing, and agile iteration.<br><br>With the spec-driven development approach, twenty integrations were fully developed, and now one gets shipped in one day instead of two weeks, with no manual code reviews.</p><h4><br>About OpenTalks.Ai</h4><p><a href="https://opentalks.ai/">OpenTalks.AI</a> is an international, independent Open conference on Artificial Intelligence. Started in 2018 with 600 participants, now it is a well-known conference on AI/ML in Western Europe, gathering 1000+ participants with a community of 5,000+ professionals.</p><div><hr></div><p style="text-align: center;"><em>Listen or watch on <a href="https://www.youtube.com/watch?v=o5vZyQnugqs">YouTube</a></em></p><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[Can LLMs x10 software development | Johan Rosenkilde | ***plain dev day 26]]></title><description><![CDATA[Watch now (48 mins) | At ***plain dev day 26, we had the honor of hosting Johan Rosenkilde, member of the original team behind GitHub Copilot and a pioneer of spec-driven development.]]></description><link>https://blog.codeplain.ai/p/can-llms-x10-software-development</link><guid isPermaLink="false">https://blog.codeplain.ai/p/can-llms-x10-software-development</guid><dc:creator><![CDATA[Codeplain]]></dc:creator><pubDate>Thu, 26 Mar 2026 16:06:31 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/192210838/067d954c9ab2930ba58ff7f3ed04a3e8.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>At <strong><a href="https://www.linkedin.com/feed/update/urn:li:activity:7426914489352130561">***plain dev day 26</a></strong>, we had the honor of hosting <strong><a href="https://www.linkedin.com/in/johan-rosenkilde/">Johan Rosenkilde</a></strong>, member of the original team behind GitHub Copilot and a pioneer of spec-driven development. In his keynote, Johan contrasted agentic coding with spec-driven development.</p><p><strong>Spec-driven development enables developers to:</strong><br> &#8226; Care about specs at 1/10 the cognitive complexity<br> &#8226; Control and predict code changes through spec changes alone<br> &#8226; Collaborate effectively at the level of specs<br><br>LLMs are amazing at writing code. But writing code is not the same as developing software. If developers need to review generated code, 10x productivity remains out of reach.<br><br>The real leverage isn&#8217;t faster typing, but it&#8217;s raising the level of abstraction.</p><div><hr></div><p style="text-align: center;"><em>Listen or watch on <a href="https://www.youtube.com/watch?v=w6sPflMxm88">YouTube</a></em></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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 *codeplain! Subscribe to receive new posts on AI-powered code generation.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[Making Meaning Explicit]]></title><description><![CDATA[The :Concept: Notation in ***plain Specifications]]></description><link>https://blog.codeplain.ai/p/making-meaning-explicit</link><guid isPermaLink="false">https://blog.codeplain.ai/p/making-meaning-explicit</guid><dc:creator><![CDATA[Dusan Omercevic]]></dc:creator><pubDate>Wed, 28 Jan 2026 13:07:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/864623d6-63b3-4b90-a216-1f8ed806d2ed_1077x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In our blog post <a href="https://blog.codeplain.ai/p/beyond-vibe-coding">Beyond Vibe Coding</a>, we introduced the ***plain specification language and argued for a fundamental shift in software development: specifications should become the source of truth for software behavior, while source code becomes a secondary, ephemeral artifact. This shift changes what specifications are for. They are no longer informal guidance written primarily for humans, but authoritative inputs that must be interpreted reliably by automated systems.</p><p>A common objection to this approach is that natural language is inherently ambiguous. Source code, the argument goes, has a single, well-defined interpretation, while prose does not. If specifications are written in natural language, how could they ever function as an unambiguous source of truth for software behavior?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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 *codeplain! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This post describes a syntactic feature of the ***plain specification language which addresses that concern. The :Concept: notation, together with a small set of accompanying rules, provides a disciplined way to define, reuse, and validate key concepts &#8212; without turning specifications into formal ontologies or rigid schemas. Where ambiguity would materially change the resulting software, it strengthens specifications by making critical meanings explicit and stable, while allowing natural language to remain expressive everywhere else.</p><h2>Ambiguity in Practice</h2><p>Let&#8217;s say a developer writes the following specification:</p><pre><code><code>Write a task manager app.</code></code></pre><p>Without additional context, at least two interpretations of this specification are possible:</p><p><em>&#8220;Write a task manager app for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.&#8221;</em></p><p><em>&#8220;Write a task manager app that manages tasks as executable system processes. Tasks should be schedulable, observable, and capable of running concurrently.&#8221;</em></p><p>While the first interpretation is more probable in most contexts, the second interpretation is admissible and may be picked by an LLM when generating software code. If we want to avoid surprises during code generation and have specifications truly be the source of truth for the software&#8217;s functionality, we need to address such situations and reduce ambiguity in specifications.</p><p>Looking closely at this specification, we can see that a large part of its meaning hinges on the single word &#8220;<em>task</em>&#8221;. We can remove most of the ambiguity in the specification if we explicitly define the meaning of the word &#8220;<em>task</em>&#8221; as:</p><pre><code>- <strong>Task</strong> describes an activity that needs to be done by the user.</code></pre><p>With this definition in place, the specification now strongly indicates that the developer&#8217;s intention is to describe a personal productivity application rather than a system for managing computational processes.</p><p>By explicitly defining the meaning of &#8220;<em>task</em>,&#8221; we have reduced ambiguity &#8212; but not eliminated it entirely. For example, the following interpretations are still both valid:</p><p><em>&#8220;Write a Node JS task manager web application for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.&#8221;</em></p><p><em>&#8220;Write a console task manager app in Python for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.&#8221;</em></p><p>We can address this other ambiguity in the specification in the same way, by explicitly defining the meaning of the word &#8220;<em>app</em>&#8221; as:</p><pre><code>- <strong>App</strong> is a Python console application.</code></pre><p>But now imagine a longer specification containing dozens of words whose meanings materially affect the resulting software. How would a developer &#8212; or an automated tool &#8212; know which words have already been explicitly defined and which still carry implicit, potentially ambiguous meaning?</p><p>In text written for humans, a common way to draw attention to key concepts is to capitalize or bold them. However, overloading Markdown syntax<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> for bold text &#8212; indicated using ** &#8212; with additional semantic meaning would be confusing for developers and fragile for automated tools</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QfWX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QfWX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QfWX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg" width="504" height="671.8846153846154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:504,&quot;bytes&quot;:1355078,&quot;alt&quot;:&quot;&quot;,&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://blog.codeplain.ai/i/185821515?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!QfWX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QfWX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c536a3-5fd3-493b-9b0a-9ecf8d0da40c_3024x4032.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><figcaption class="image-caption">A page from Domain-Driven Design with key concepts explicitly marked in bold.</figcaption></figure></div><p>Instead, ***plain introduces a dedicated notation for concepts like &#8220;<em>task</em>&#8221; and &#8220;<em>app</em>&#8221; that need meaning explicitly defined. After evaluating several alternatives with respect to readability, ease of parsing, and low collision with natural language, we adopted a notation in which concept names are enclosed in colons at both the beginning and the end.</p><p>Using this convention, our running example becomes:</p><pre><code>- <strong>:App:</strong> is a Python console application.

- <strong>:Task:</strong> describes an activity that needs to be done by the user.

Write <strong>:App:</strong> for managing <strong>:Task:</strong> items.</code></pre><p>Concept names must not contain spaces and may include only letters, digits, and a limited set of special characters such as dots and underscores<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>. This constraint ensures that concepts are easy to recognize programmatically while remaining readable to humans.</p><p>The goal of :Concept: notation is not to eliminate natural language or replace it with a formal ontology, but to provide a mechanism for anchoring meaning where ambiguity is costly. It is a precision tool rather than a requirement: words like &#8220;<em>console</em>,&#8221; &#8220;<em>describes</em>,&#8221; or &#8220;<em>activity</em>&#8221; may appear without special notation when the developer does not intend to constrain their interpretation.</p><h2>Reducing Ambiguity with :Concept: Notation</h2><p>:Concept: notation helps developers reduce ambiguity in specifications. Without explicit rules, however, concept annotations would risk becoming purely decorative, adding visual emphasis without enforceable semantic guarantees. To avoid this, and to ensure that explicit concepts can function as reliable anchors of meaning, ***plain defines a small set of rules that govern how concepts written in :Concept: notation are introduced and used. These rules are intentionally minimal. Their purpose is not to constrain how specifications are written in general, but to establish the conditions under which ambiguity around key concepts can be detected and addressed explicitly.</p><p>In ***plain, concepts written using :Concept: notation are governed by the following rules:</p><ol><li><p><strong>Concepts must be defined before they can be used.</strong></p></li><li><p><strong>Definitions must begin with a concept name.</strong></p></li><li><p><strong>Concepts must not be redefined.</strong></p></li><li><p><strong>Concept names are case sensitive.</strong></p></li><li><p><strong>Concepts must not change meaning through use.</strong></p></li></ol><p>Together, these rules establish the conditions under which concepts written in :Concept: notation can be reliably checked by automated tools and consistently used by software developers, making ambiguity in specifications easier to detect in practice.</p><p>A detailed discussion of these rules and the reasoning behind them is available in the whitepaper <a href="https://plainlang.org/docs/whitepapers/on-the-concept-of-concept">On the Concept of :Concept:</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mlAl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mlAl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 424w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 848w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 1272w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mlAl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png" width="1456" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:218030,&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://blog.codeplain.ai/i/185856248?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.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_!mlAl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 424w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 848w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 1272w, https://substackcdn.com/image/fetch/$s_!mlAl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8de6dd0a-782c-4710-89a4-93b6df93c0f1_1898x1112.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">A screenshot of an IDE where a concept is renamed using refactoring functionality.</figcaption></figure></div><h2>From Incremental Prompts to Coherent Concepts</h2><p>Consider a developer who already has an initial version of the task manager app and now wants to refine it based on user feedback. When using agentic AI development tools such as Claude Code, this refinement typically happens through a sequence of incremental prompts. For example, the developer might issue prompts like:</p><ol><li><p><em>&#8220;Task must have a name.&#8221;</em></p></li><li><p><em>&#8220;Task has a due date&#8221;.</em></p></li><li><p><em>&#8220;Name is a required attribute.&#8221;</em></p></li><li><p><em>&#8220;Add notes to a task.&#8221;</em></p></li><li><p><em>&#8220;The name must be at least 3 characters long.&#8221;</em></p></li><li><p><em>&#8220;Due date is optional.&#8221;</em></p></li><li><p><em>&#8220;Name is a short description of the task.&#8221;</em></p></li><li><p><em>&#8220;Notes store additional details about the task.&#8221;</em></p></li></ol><p>Each prompt is reasonable in isolation and, taken together, they fully describe the intended behavior of a task. However, neither the human developer nor the AI generating code ever sees a single, consolidated definition of what a task is in this system.</p><p>Instead, the meaning of &#8220;<em>task</em>&#8221; accumulates implicitly over time. Its structure, constraints, and semantics emerge indirectly from scattered instructions and are eventually encoded across constructors, validation logic, database schemas, and UI components. There is no authoritative place where the concept itself is defined. This is especially problematic given the limited context window of LLMs: when a concept is defined only implicitly, parts of its effective definition may fall outside the active context or receive insufficient attention during code generation.</p><p>For humans, implicit concept definitions increase cognitive load and fragility. When a concept has no explicit definition, its meaning must be reconstructed from scattered fragments across code and specifications, rather than consulted in a single authoritative place, making inconsistencies and accidental divergence more likely over time.</p><p>In ***plain, this pattern is replaced by an explicit, consolidated concept definition. A concept can be defined with as much precision as necessary, and that definition becomes the authoritative source of truth for its meaning.</p><p>Returning to our example, all of the requirements previously scattered across incremental prompts can be captured in a single definition of :Task:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><pre><code>- <strong>:Task:</strong> describes an activity that needs to be done by the user.
  - <strong>:Task:</strong> has the following attributes:
    - Name - a short description of <strong>:Task:</strong>
      - This is a required attribute.
      - The name must be at least 3 characters long.
    - Notes - additional details about <strong>:Task:</strong>
    - Due Date - optional date by which to complete <strong>:Task:</strong></code></pre><p>Rather than extending the concept implicitly through a sequence of prompts, this definition extends it explicitly and holistically. All essential properties, constraints, and relationships are visible in one place. This allows developers to reason about the concept as a whole, detect inconsistencies early, and evolve requirements without fragmenting meaning across prompts or code.</p><p>Explicit concept definitions also provide a much stronger semantic signal to automated tools. Instead of inferring structure and invariants from scattered instructions, an AI system can derive them directly from the concept definition itself. From the :Task: definition above, an implementation naturally follows, for example, a Python data class with clearly defined attributes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nEj-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nEj-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 424w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 848w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 1272w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nEj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png" width="1066" height="282" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:282,&quot;width&quot;:1066,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26731,&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://blog.codeplain.ai/i/185856248?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.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_!nEj-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 424w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 848w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 1272w, https://substackcdn.com/image/fetch/$s_!nEj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9846d3d0-a05a-4e22-a035-57059bb43567_1066x282.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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>Real software systems often contain dozens or even hundreds of such concepts, each with its own structure, constraints, and relationships. If these concepts are specified only implicitly through sequences of incremental prompts and represented only in generated code, the likelihood of misunderstanding grows steadily. Meaning becomes distributed, incomplete, and increasingly difficult to reconstruct, both for humans and for automated systems.</p><p>The :Concept: notation in ***plain addresses this problem directly. By giving concepts explicit names and dedicated definitions, it provides stable places where meaning can be accumulated intentionally rather than inferred indirectly. Concepts are not merely disambiguated, but progressively enriched, capturing structure, constraints, and relationships in a single authoritative form. As specifications grow, meaning becomes more precise rather than more fragile, remaining centralized in a single definition and avoiding semantic drift or silent forking across the system.</p><p>Explicit concept definitions also help coordinate understanding in larger software development projects, where specifications grow beyond what any single person can fully grasp. When concepts are defined only implicitly, different team members inevitably form different mental models based on the parts they work on. A single, authoritative definition provides a shared reference point, allowing teams to align on meaning, discuss changes precisely, and detect divergences early before they are encoded inconsistently into the software code<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>.</p><h2>Conclusion</h2><p>When specifications are treated as informal guidance, ambiguity is tolerable and often ignored. But when specifications become the source of truth for software behavior and the primary input to automated code generation, ambiguity becomes a liability. What was once a matter of interpretation turns into a source of silent and compounding divergence between intent, implementation, and outcome.</p><p>The :Concept: notation in ***plain is a pragmatic response to this shift. It does not attempt to formalize all meaning or replace natural language with rigid schemas. Instead, it provides a lightweight mechanism for anchoring meaning where precision matters most. By making key concepts explicit, stable, and reusable, it allows developers to commit to intent early, detect ambiguity where it is costly, and retain the expressive power of natural language everywhere else.</p><p>As specifications grow and evolve, explicit concepts offer a coherent place to accumulate understanding, reason about change, and integrate new requirements without fragmenting meaning across prompts, files, or generated code. For humans, they serve as shared reference points. For automated tools, they provide reliable semantic anchors that reduce guesswork and improve correctness.</p><p>Making meaning explicit is not an exercise in formalism. It is a recognition that when software is generated from specifications, meaning directly shapes the result &#8212; and must be handled with the same discipline as code.</p><div><hr></div><p><em><a href="https://www.codeplain.ai/">*codeplain</a> will soon release the first version of its platform based on the **plain specification language. If you want to be among the first to use :Concept: notation in practice, you can join the waitlist to get early access and updates.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.codeplain.ai/#waitlist&quot;,&quot;text&quot;:&quot;Join the waitlist&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://www.codeplain.ai/#waitlist"><span>Join the waitlist</span></a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>***plain specification language uses Markdown as its base syntax.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>See the whitepaper <a href="https://plainlang.org/docs/whitepapers/on-the-concept-of-concept">On the Concept of :Concept: </a>for a definition of which ASCII characters are permitted in concept names.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>A full example of the console task manager Python application is available at <a href="https://github.com/Codeplain-ai/console-task-manager">https://github.com/Codeplain-ai/console-task-manager</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>This use of explicit concept definitions is closely related to the idea of Ubiquitous Language from Domain-Driven Design, which emphasizes shared, precise terminology as a coordination mechanism among developers and domain experts.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[DevRev's transition to AI-powered integration development]]></title><description><![CDATA[How *codeplain is helping DevRev automate integration development with spec-driven development.]]></description><link>https://blog.codeplain.ai/p/devrevs-transition-to-ai-powered</link><guid isPermaLink="false">https://blog.codeplain.ai/p/devrevs-transition-to-ai-powered</guid><dc:creator><![CDATA[Kaja Skerlj]]></dc:creator><pubDate>Tue, 18 Nov 2025 13:59:02 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cfa249d4-5cf6-422d-9d6a-80f4eca083a5_1260x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Over the past year, the rise of <strong>AI-powered coding</strong> has changed how product teams build software. As models became capable of handling more of the implementation work, engineers began shifting focus from repetitive coding toward higher-level questions of architecture, validation, and intent. The result is a quiet change in how teams collaborate and moving away from manual implementation toward a <strong>shared language of specification</strong>.</p><p>With the rise of vibe coding and spec-driven development, the boundaries between roles are shifting. Product designers are now using vibe coding tools to build entire features and screens directly in code, exploring behavior instead of just using static interfaces. Engineers, meanwhile, shifted the focus from writing code to <strong>describing the system so that machines could build it reliably.</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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 *codeplain! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This makes a fundamental change in how teams plan, design, and execute work&#8212;one that aligns closely with <em>Marty Cagan&#8217;s</em> idea of <strong>empowered product teams</strong>, where design, product, and engineering operate as peers defining outcomes, not just deliverables.</p><p>At *codeplain, we saw these ideas come to life most vividly in our work with <a href="https://devrev.ai/">DevRev</a>. Their journey from manual integration development to <strong>automated specification-based development</strong> became a live demonstration of this shift.</p><h2><strong>Inside the DevRev Partnership</strong></h2><p>Six months ago, our team at *codeplain partnered with <strong>DevRev</strong>, <strong>an</strong> <strong>AI-first product company</strong> that connects customer support, product management, and engineering into a single workflow. Their powerful <strong>AirSync integration <a href="https://devrev.ai/docs/product/core">platform</a></strong> powers hundreds of connections to third-party tools like Slack, Jira, GitHub, Salesforce, Google Drive, and Microsoft Outlook &#8212; ensuring that data flows seamlessly between teams and systems.</p><p>Our goal was to introduce a specification-driven approach to integration development &#8212; replacing manual coding with structured, executable definitions written in  <a href="https://blog.codeplain.ai/p/beyond-vibe-coding">***plain specification language</a>. In this new model, integrations could be implemented and tested entirely through specifications, with *codeplain <strong>automatically generating production-ready, test-covered code</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_!UIz4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UIz4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 424w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 848w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 1272w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UIz4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp" width="728" height="436.8" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:864,&quot;width&quot;:1440,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:64066,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.codeplain.ai/i/178869246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UIz4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 424w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 848w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 1272w, https://substackcdn.com/image/fetch/$s_!UIz4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d4887df-8b4f-4c3b-9fd0-9902976c489f_1440x864.webp 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"><a href="https://devrev.ai/meet-computer">Computer</a>, by DevRev, combines structured data with unstructured data to solve one of AI&#8217;s hardest problems: breaking down data silos and giving AI the complete context it needs.</figcaption></figure></div><p>From my perspective as a product designer at *codeplain, this partnership wasn&#8217;t just about code automation, but about <strong>redefining how teams collaborate with AI and with each other</strong>. I wanted to understand how engineers experience that shift: how it changes debugging, testing, and even the sense of ownership over the system they&#8217;re building.</p><p>Throughout the six-month collaboration, we worked closely with two DevRev engineers, Patricija and Ga&#353;per, who became our closest partners in shaping this new process. Patricija was the first to adopt *codeplain for integration development, exploring how <strong>acceptance tests</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> could serve as both validation and debugging tools through implementing Wrike. Ga&#353;per, meanwhile, focused on observability through implementing Aha! by building a custom command-line interface to better understand how the ***plain rendering and generation pipeline behaved in real time.</p><p>Together, we created a repeatable and scalable process for integration development using specifications. What follows is the story of that transformation: how DevRev moved from hand-coded integrations to fully automated, specification-driven systems &#8212; and what that change looked like from the inside.</p><h1><strong>Developing a repeatable and scalable process</strong></h1><p>When we began working with DevRev, we decided to introduce spec-driven development incrementally. We wanted to verify, at each stage, that *codeplain could meet DevRev&#8217;s engineering standards for <strong>structure, reliability, and maintainability</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Toph!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Toph!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 424w, https://substackcdn.com/image/fetch/$s_!Toph!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 848w, https://substackcdn.com/image/fetch/$s_!Toph!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 1272w, https://substackcdn.com/image/fetch/$s_!Toph!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Toph!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png" width="1456" height="172" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:172,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28433,&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://blog.codeplain.ai/i/178869246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.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_!Toph!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 424w, https://substackcdn.com/image/fetch/$s_!Toph!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 848w, https://substackcdn.com/image/fetch/$s_!Toph!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 1272w, https://substackcdn.com/image/fetch/$s_!Toph!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b9b5f1e-01f0-4e4f-86e4-d21f09f19538_1497x177.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The process unfolded in <strong>five distinct phases</strong>, each validating a core principle of spec-driven development.</p><h2>1. Reimplementing an Existing Integration</h2><p>Our starting point was to <strong>prove functional equivalence</strong>. The engineers reimplemented one of the existing integrations, Asana, using ***plain specification language. The goal wasn&#8217;t innovation, but verification: could a spec-driven workflow replicate the logic, SDK structure, performance, and integration framework of manually written code?</p><p>We started by decomposing the entire integration into <strong>a set of</strong> <strong>functional requirements</strong>, each representing a small, testable unit of behavior. Every integration begins with making API calls to a third-party service, processing the response, and mapping data to DevRev data models. In ***plain, we captured each of these as a <strong>reusable building block</strong>, defining inputs, outputs, and validation conditions explicitly.</p><p>Here&#8217;s an example of how to fetch a list of projects in the Asana workspace in ***plain:</p><pre><code><em>- Implement the :Function: that fetches the :ListofProjects: using the projects endpoint of the :API:.</em></code></pre><p>By defining integrations this way, the developer stayed in control of the <strong>high-level architecture and behavior</strong>, while *codeplain handled the implementation details and boilerplate. It shifted engineers&#8217; attention to architectural decisions, leaving the repetitive implementation details to the code renderer.</p><p>The results demonstrated that *codeplain could generate <strong>functionally equivalent, production-ready code</strong> &#8212; delivering the same behavior, reliability, and maintainability as the manually built version, while eliminating repetitive boilerplate work.</p><h2>2. Implementing a New Integration</h2><p>After proving that <em>*</em>codeplain could reproduce an existing integration, we moved on to the real test: <strong>could we build a brand-new integration and do it with enterprise-level precision?</strong></p><p>DevRev chose Trello as the first new integration to build with ***plain, since it&#8217;s a core part of DevRev&#8217;s AirSync platform. Engineers defined the integration entirely in specifications&#8212;data mappings, event triggers, API endpoints, and expected behavior&#8212;leaving *codeplain to generate the implementation.</p><p>Here&#8217;s an example of how to fetch boards in the Trello workspace in ***plain:</p><pre><code>- Implement the :Function: that fetches the :ListofBoards: using the boards endpoint of the :API:.</code></pre><p>Just like in Asana, the integration boiled down to the same basic pattern: call the third-party API, process the response, and map the data into DevRev&#8217;s models. The specifics differed, but the structure was almost identical.</p><p>That similarity turned out to be important. Seeing Asana and Trello expressed as sets of functional requirements made it clear that most of the integration follows a <strong>repeatable shape</strong>. The only thing that really changes is the third-party service itself&#8212;a realization that would shape the next phase.</p><p>This step demonstrated that *codeplain could deliver new integrations that met DevRev&#8217;s engineering standards for performance, structure, and reliability. It also showed that engineers could now focus on defining intent and specifications instead of writing and reviewing code line by line.</p><h2>3. Creating an Integration Template</h2><p>After completing Asana and Trello, we had enough evidence that the approach worked: *codeplain could generate integrations that behaved the way DevRev expected, and the workflow held up across multiple services.</p><p>With two specs to compare, a pattern started to emerge. Even though Asana and Trello are very different products, the structure of the integration was largely the same. The team kept seeing the same building blocks repeat:</p><ul><li><p>Standardized credentials</p></li><li><p>Boilerplate API schema definitions</p></li><li><p>Authentication and metadata synchronization logic</p></li><li><p>Reusable data-fetching</p></li></ul><p>These similarities pointed to a simple conclusion: <strong>most of the integration doesn&#8217;t change. Only the third-party API does.</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_!l4gE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l4gE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 424w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 848w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 1272w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l4gE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png" width="1092" height="411" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:411,&quot;width&quot;:1092,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41953,&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://blog.codeplain.ai/i/178869246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.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_!l4gE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 424w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 848w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 1272w, https://substackcdn.com/image/fetch/$s_!l4gE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c545e60-7990-4cdb-9d67-752b8804b97a_1092x411.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">The diagram shows how DevRev standardized multiple integrations into a reusable integration template, which *codeplain&#8217;s AI then used to automatically generate production-ready integration code.</figcaption></figure></div><p>To accelerate integration development, DevRev pulled the shared pieces into a <strong>modular integration template</strong>. All the common logic moved into the template, while each new integration kept only the service-specific parts&#8212;its endpoints, authentication parameters, and API payloads. The rest was handled automatically.</p><p>This shift created a <strong>scalable process for AirSync development</strong>. Engineers no longer wrote integrations line by line; they defined how the integration should behave, and the system generated the implementation around that intent. It turned what had been a repetitive engineering workflow into a consistent, repeatable process&#8212;while keeping engineers in control of the architecture.</p><h2>4. Testing the Template on New Integrations</h2><p>Once the integration template was stable, DevRev set out to test its scalability across different APIs and authentication models. Engineers Patricija and Ga&#353;per worked independently, each implementing a new integration in parallel. Both integrations confirmed that integrations could be <strong>built, tested, and deployed in under two days</strong> using ***plain.</p><p>As more of the workflow became automated, a new issue surfaced: engineers needed better visibility into what the system was actually doing. When rendering, testing, and validation all happen behind the scenes, it can be hard to understand where something failed or why a run took longer than usual.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MaD4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MaD4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 424w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 848w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 1272w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MaD4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png" width="1456" height="508" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:508,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47352,&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://blog.codeplain.ai/i/178869246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.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_!MaD4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 424w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 848w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 1272w, https://substackcdn.com/image/fetch/$s_!MaD4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F367c859d-2b08-417e-95e7-500d0dd5bc1a_1497x522.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">Example of ***plain specification integration template used to define data mappings.</figcaption></figure></div><p>To address this, Ga&#353;per built a lightweight command-line tool that visualized each stage of the generation pipeline with timestamps. It replaced a stream of raw logs with a clearer view of the rendering flow, making debugging faster and helping engineers trust the system&#8217;s behavior. This tool became the foundation for <strong>improving developer observability</strong> inside automated workflows, something we&#8217;ve been improving at *codeplain ever since.</p><p>By the end of this step, the engineering workflow itself was fast and repeatable. Once a developer had a clear ***plain specification, they could generate and validate an integration without touching boilerplate code.</p><p>But this only solved the engineering side of the process. Engineering could now move quickly, but product inputs were too often still arriving through ad-hoc conversations, Slack threads, and scattered notes. In other words, the product organization now needed to provide requirements faster and in a more structured way. That gap is what ultimately led to the introduction of a PRD-driven workflow.</p><h2>5. Introducing a PRD-Driven Workflow</h2><p>As DevRev&#8217;s engineering speed increased, a new challenge emerged &#8212; <strong>the bottleneck had shifted upstream</strong>. Specs could now be generated and deployed in days, but only after someone had precisely defined what the system should do. The faster engineering moved, the more the product had to catch up.</p><p>To make that alignment part of the process, DevRev is restructuring around a <strong>Product Requirements Document (PRD)</strong>. The PRD sits at the same abstraction level as an integration specification &#8212; it defines what the system should do, how it behaves, and what external dependencies it touches.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!618R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!618R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 424w, https://substackcdn.com/image/fetch/$s_!618R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 848w, https://substackcdn.com/image/fetch/$s_!618R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 1272w, https://substackcdn.com/image/fetch/$s_!618R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!618R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png" width="1456" height="963" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:963,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122169,&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://blog.codeplain.ai/i/178869246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.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_!618R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 424w, https://substackcdn.com/image/fetch/$s_!618R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 848w, https://substackcdn.com/image/fetch/$s_!618R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 1272w, https://substackcdn.com/image/fetch/$s_!618R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3a6abe2-6236-420d-b348-bb570d93e8e7_1497x990.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">Example of a PRD that bridges product thinking and engineering execution, ensuring that the resulting ***plain specification accurately reflects business logic and user needs.</figcaption></figure></div><p>Instead of writing specs first and adjusting decisions afterward, teams iterated on the PRD until it captured the complete intent. From there, engineers could automatically generate most of the ***plain specification, refine any missing details, and validate it through automated tests.</p><p>This change didn&#8217;t just speed things up, but also created a shared foundation between product and engineering. The PRD turned intent into something concrete, making specification-driven development a natural extension of how teams already plan and design features.</p><h2><strong>Why Teams Like DevRev Are Adopting Spec-Driven Development Now</strong></h2><p>Working with DevRev over the past six months has made one thing clear: <strong>spec-driven development isn&#8217;t just a new engineering technique, but a structural necessity for AI-powered organizations.</strong></p><p>Modern product teams can&#8217;t keep growing headcount every time complexity increases. The old model &#8212; growing engineering headcount in proportion to product ambition &#8212; no longer works. The constraint is no longer code; it&#8217;s collaboration. As systems multiply, so do handoffs, reviews, and the time it takes to align on intent. Automation helps, but abstraction is what truly unlocks scale.</p><p>Spec-driven development gives teams that abstraction. By capturing intent in structured specifications, engineers focus on architecture and validation rather than boilerplate, while product and design define behavior upfront. Designers and product managers define behavior and constraints upfront, ensuring alignment before a single line of code is generated.</p><p>The quiet shift we saw at DevRev is part of a broader change: teams are learning to collaborate with AI at the intent layer, not compete with it at the implementation layer. What started as an experiment in automation has evolved into a new foundation for building software &#8212; <strong>where the specification, not the codebase, is the real source of truth.</strong></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Acceptance tests verify requirements and validate user needs.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[From Vibes to Specs]]></title><description><![CDATA[Two Ways I Built the Same Search-and-Replace CLI Tool]]></description><link>https://blog.codeplain.ai/p/from-vibes-to-specs</link><guid isPermaLink="false">https://blog.codeplain.ai/p/from-vibes-to-specs</guid><dc:creator><![CDATA[Tjaž Eržen]]></dc:creator><pubDate>Tue, 07 Oct 2025 12:01:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/70217521-f8e7-4b00-91a5-08920b964a9c_1260x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Vibe coding is often the fastest way to get an idea off the ground. Even though I work at *codeplain, where we focus on spec-driven development, I still vibe-code all the time.</p><p>One of those moments came up in a Node.js project I was working on, where I realized the LLM had hard-coded testing secrets into my repo. Manually patching each file felt tedious and error-prone. Instead, I saw an opportunity to automate it: Build a CLI that takes a target folder and a YAML secrets mapping, then recursively replaces real secrets with mocked ones. I called it <strong>Search-and-Replace CLI</strong>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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 *codeplain! 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>My goal was to build the CLI quickly and reliably without spending too much time writing code by hand. Vibe coding &#8212; building by chatting with an AI and iterating in natural language &#8212; is my usual way to get projects off the ground fast. However, I also wanted to see if a structured, test-backed approach could save me from the trial-and-error loop I usually hit with vibe coding.</p><p>So I built the same CLI twice &#8212; once by vibes, once by specs &#8212; to find out.</p><h2>Approach (a): Vibe Code the CLI</h2><p>My first approach was vibe coding. I wrote a prompt for Cursor, hoping for a one-shot solution.</p><pre><code><code>Goal: Recursively replace secrets in all text files of a target folder

Inputs:
  --target-folder &lt;path&gt; (validate path exists)
  --secrets-file &lt;yaml&gt;. See secrets_config.yaml for the schema.

Rules:
  - Python CLI
  - Read YAML mappings (real &#8594; mock)
  - Skip binaries
  - Overwrite files in place
  - Log processed files and replacement counts for every file to stdout</code></code></pre><p>Cursor produced a runnable draft in two minutes. In order to validate the produced code, I manually created test folder fixtures (e.g., <code>example1-no-replace/</code>, <code>example2-replace-secret2/</code>), ran the CLI while inspecting the diffs between outputs and fixtures.</p><p>The first snag was incorrect YAML schema validation of the produced code. Fixing it took a few iterative passes with the LLM and hand checks to confirm the mapping rules. Once &#8220;working,&#8221; I tried a refactor for readability, which broke earlier cases and sent me back into the generate &#8594; test &#8594; fix loop. After a couple of attempts, I reverted to the &#8220;working&#8221; version and retried the refactoring step.</p><p>Regenerations weren&#8217;t stable either. In one run, Cursor scaffolded test folders for me; in the next, it dropped them entirely. YAML validation appeared in some versions and disappeared in others. This variability meant I had to rely on manual testing for every change, since I couldn&#8217;t assume consistency between generations.</p><p>In short, vibe coding gave me speed up front &#8212; I had a running CLI in minutes &#8212; but without guardrails to ensure consistency, each edit risked breaking what was already working. Manual testing became the only way to build trust in the tool, and that quickly turned into the bottleneck.</p><div><hr></div><h2>Approach (b): Spec&#8209;Driven</h2><p>For the second attempt, I built the search-and-replace CLI with <a href="https://blog.codeplain.ai/p/beyond-vibe-coding">***plain specification language</a> we&#8217;re developing at *codeplain. Unlike vibe coding, where the AI generates code directly from a prompt, ***plain starts from a specification written in extended Markdown<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>.</p><p>I broke the work into three verifiable units:</p><ol><li><p><strong>CLI scaffold</strong> &#8212; set up the entry point and command structure. ***plain supports templating, so this will be automatically added with the &#8220;include&#8221; statement below.</p></li><li><p><strong>Arguments &amp; config</strong> &#8212; parse --target-folder and --secrets-file</p></li><li><p><strong>Recursive replace engine</strong> &#8212; walk the directory tree and replace in place</p></li></ol><p>In ***plain, we refer to each &#8220;building block&#8221; as a functional requirement. The decomposition in smaller units converts to the following ***plain specification<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>:</p><pre><code>{% include &#8220;python-console-app-template.plain&#8221;, main_executable_file_name: &#8220;search_and_replace.py&#8221; %}

# Search and replace recursively

***Definitions:***

- The Secrets File is a YAML file that contains a mapping from the real secrets to the mock secrets.
- The Target Folder is a folder that contains the files in which to replace secrets.

***Functional Requirements:***

- The App should accept options `--target-folder` (The Target Folder) and `--secrets-file` (The Secrets File). The Secrets File [secrets_config.yaml](secrets_config.yaml) contains the expected schema of The Secrets File.

- The App should replace all original secrets with mock values recursively across The Target Folder.</code></pre><p>For each functional requirement (FR), *codeplain generates not only the implementation code but also unit and conformance tests, and then runs them. It doesn&#8217;t proceed to the next FR until the current tests pass. When starting a new FR, all previously generated tests are run to catch regressions early.</p><p>For example, if the generation of FR3 (&#8220;replace recursively&#8221;) breaks the FR2 (&#8220;reading <code>--target-folder</code> and <code>--secrets-file</code>&#8221;), its tests fail and halt progress until corrected &#8212; a guardrail that matters more as the project grows.</p><p>In ***plain, both implementation and conformance tests come from the same specification, but the conformance tests are generated independently. They treat the program as a black box and verify only observable outcomes &#8212; exit codes, logs, and file changes &#8212; against the spec, not its implementation code.</p><p>This all happens with the specification as the source of truth: The implementation is derived from the spec, so understanding the spec maps directly to understanding the program&#8217;s behavior.</p><p>After about ten minutes of generation and tests, I ran the CLI on <code>example1-no-replace/</code> and <code>example2-replace-secret2/</code> and it produced the expected replacements on the first run.</p><p>Building with specs takes a bit more upfront investment to write and render, but results in more reliable code generation and less time-consuming iterations.</p><h2>Updating the Search-and-Replace CLI</h2><p>After testing both versions on sample folders, I tried running both CLIs on my Node.js project. Both versions immediately got stuck replacing strings in <code>node_modules/</code> &#8212; a folder that should clearly be ignored.</p><p>With vibe coding, the codebase is the source of truth, so every fix triggers regeneration and a full manual diff review. Even though I accepted 95% of the generated code as-is, I still had to review the entire 100%. Excluding <code>node_modules/ </code>meant regenerating, reviewing the diff, and manually rerunning fixtures to verify the change.</p><p>With spec-driven development, the spec is the source of truth: update the spec and rerender only the affected parts. Instead of &#8220;prompt-and-pray&#8221;, we update the relevant functional requirement (FR3) and rerender just that portion.</p><pre><code>- The App should replace all original secrets with mock values recursively across The Target Folder, <strong>excluding the node_modules/ directory.</strong></code></pre><p>Automatically generated conformance tests assert that files under <code>node_modules/</code> are never modified. With that constraint in place, the tool did what I intended: safely search-and-replace secrets without touching the <code>node_modules/</code> folder.</p><p>To summarize, here&#8217;s how steering the expected behaviour differs between vibe coding and spec-driven development:</p><ul><li><p><strong>Vibe coding</strong>: New prompt &#8594; update implementation &#8594; review a large diff &#8594; run manual tests for regressions.</p></li><li><p><strong>Spec-driven</strong>: Update/add a FR &#8594; rerender impacted FRs &#8594; proceed with confidence.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f8ej!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f8ej!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 424w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 848w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 1272w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f8ej!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png" width="1450" height="704" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:704,&quot;width&quot;:1450,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:77849,&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://blog.codeplain.ai/i/174449613?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.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_!f8ej!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 424w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 848w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 1272w, https://substackcdn.com/image/fetch/$s_!f8ej!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89bd1718-3968-40d3-8ef3-9972795372d8_1450x704.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">A table of a head-to-head comparison between vibe coding and spec-driven development.</figcaption></figure></div></li></ul><p>Building the same tool twice &#8212; once with vibes and once with specs &#8212; turned out to be a valuable learning experience in itself. This experiment showed me that vibe coding and spec-driven development aren&#8217;t opposites&#8212;they&#8217;re complementary approaches with different trade-offs.</p><p>While building the CLI, vibe coding gave me speed up front: I had a working CLI in minutes and could explore quickly. But without guardrails, every edit risked breaking something, and manual testing soon became the bottleneck.</p><p>Spec-driven development took a little more upfront effort, but once the specification was in place, the generated functionality was deterministic. Automatically generated tests provided guardrails that the generated code fully conforms to the specs. When I needed to exclude <code>node_modules/</code>, I<code> </code>only had to update the spec, while the tests gave me confidence that nothing else broke.</p><p>The trade-off is clear: vibe coding is great for hacking together ideas or prototypes, while spec-driven development shines when reliability, collaboration, and trust matter. Think Python: interactive mode when you&#8217;re exploring, but shifting to scripting mode when the idea matures. The point isn&#8217;t either/or &#8212; it&#8217;s choosing the right tool at the right time.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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://blog.codeplain.ai/subscribe?"><span>Subscribe now</span></a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><a href="https://substack.com/@codeplain/p-172083270">Beyond Vibe Coding: Introducing ***plain</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>The include statement here adds the CLI scaffold (FR1) and sets search_and_replace.py as the entry point.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Beyond Vibe Coding]]></title><description><![CDATA[Introducing ***plain, the Language of Spec-Driven Development]]></description><link>https://blog.codeplain.ai/p/beyond-vibe-coding</link><guid isPermaLink="false">https://blog.codeplain.ai/p/beyond-vibe-coding</guid><dc:creator><![CDATA[Dusan Omercevic]]></dc:creator><pubDate>Tue, 02 Sep 2025 09:26:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f26f0ed8-7c77-4bf7-a70f-7139f94c7457_1260x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes, coding is the most efficient way of specifying functionality. If we look at the following algorithm, it is much easier to express it in code:</p><pre><code>def func_A(n):
   return 2 if n == 1 else n * func_A(n // 3 + 1)</code></pre><p>than in words:</p><pre><code>Write a function that multiplies n by smaller and smaller integers obtained by integer-dividing by 3 and adding 1 each time, until it reaches 1, at which point it uses 2 instead of 1 in the multiplication.</code></pre><p>When precision and control are essential, code gives the developer ultimate power.</p><p>But such power is rarely needed. Outside of the domain of non-standard algorithms and mathematical expressions, <strong>specifying by coding</strong> is cumbersome and inefficient in describing the desired functionality. For example, the code below:</p><pre><code>def fetch(limit=100):
   headers = {"Authorization": f"Bearer {API_KEY}"}
   cursor = None
   users = []

   while True:
       params = {"limit": limit}
       if cursor:
           params["cursor"] = cursor

       r = requests.get(f"{BASE_URL}/v1/users", 
               headers=headers, params=params)
       r.raise_for_status()

       payload = r.json()
       users.extend(payload.get("data", []))
       cursor = payload.get("next_cursor")
       if not cursor:
           break

   return user</code></pre><p>is much harder for a human to write and understand than this natural language specification:</p><pre><code>Fetch the list of users from the API.</code></pre><p>Even if we want to be more specific in guiding the implementation, it is more efficient to specify the preferred approach (e.g., implement paging using cursors) than to code it ourselves.</p><p>Only in computer programming have we turned to a fully artificial language to express intent. Even in mathematics, most of a paper or proof is written not in formulas but in words. Laws, too, are composed in natural language, and attempts to formalize them have succeeded only in the simplest of cases. No programming language approaches the expressiveness of natural language or the way it aligns with how the human mind works.</p><div class="pullquote"><p>No programming language approaches the expressiveness of natural language or the way it aligns with how the human mind works.</p></div><p>Inefficiency is not the only issue with specifying software functionality through code. To increase productivity and improve maintainability, developers naturally strive to reuse code and share logic. However, when reuse happens at the level of shared code, it leads to <strong>functionality blending</strong>, that is, it becomes difficult to isolate in code where one functionality ends and another begins. Because multiple functionalities are intertwined, modifying the implementation code can lead to unintended side effects &#8212; software bugs in other functionalities that share the same code paths. Cross-cutting concerns (e.g., logging, validation, error handling) further complicate the codebase, making software maintenance increasingly difficult.</p><p>While coding, developers also clarify in their mind the intent behind their decisions. But once the code is written and the mental model behind the code fades, recovering that intent can be difficult &#8212; even when the code is well-documented with semantic identifiers and comments. This becomes especially challenging when functionality is distributed across multiple locations in the codebase. A developer looking to fix a bug or implement a new functionality must first reconstruct the original intent to avoid introducing new issues. The <strong>loss of intent</strong> is a major reason why developers dislike maintaining legacy systems and why it takes so long to become productive in a new codebase.</p><h2>Vibe Coding: Fix One Thing, Break Ten Others</h2><p>Vibe coding<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>  is the practice of building software by chatting with the AI &#8212; nudging features, fixing bugs, and shaping the app &#8212; instead of hand-coding everything yourself. The value of vibe coding over classical coding is that it leverages the full expressivity of the natural language for specifying intended functionality. In most cases, this is much more efficient than specifying by coding.</p><p>Vibe coding operates in a <strong>task-oriented</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> mode: the developer describes a task in natural language, and the AI generates code to implement it. Once the code has been generated, the natural language instructions become obsolete, leaving the code as the sole source of truth for how the software behaves.</p><p>If it turns out that an earlier task was incorrectly specified or implemented, the developer must either attempt to correct it by submitting new tasks or fix the issue directly in the code. If these approaches fail or take too long, the only remaining option may be to revert to a previous working version of the code, losing all the work made in subsequent steps.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.reddit.com/r/SaaS/comments/1lp3scg/i_am_giving_up/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j6Kq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 424w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 848w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 1272w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j6Kq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png" width="1456" height="912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:912,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:433378,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://www.reddit.com/r/SaaS/comments/1lp3scg/i_am_giving_up/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.codeplain.ai/i/172083270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.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_!j6Kq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 424w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 848w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 1272w, https://substackcdn.com/image/fetch/$s_!j6Kq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feacab836-90ed-41d8-88f7-0d61e60f7123_2394x1500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">A typical example of a vibe coding experience.</figcaption></figure></div><p>Fixing issues by submitting new tasks often feels like playing whack-a-mole &#8212; resolve one bug, and two more appear<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>. The root problem is that vibe coding lacks a reliable way to rescind or alter the result of a previous task, short of rolling back to an earlier working version. Even if you instruct the AI to &#8220;undo&#8221; a change, there&#8217;s no guarantee it will fully comply. Functionality blending makes this even worse: once subsequent instructions layer new logic on top of the old, the code becomes so intertwined that reversing specific changes without collateral damage is nearly impossible.</p><p>Compounding the problem is that vibe coding lacks tools to detect and resolve ambiguities in specifications or conflicts between specifications issued across different tasks. This becomes especially problematic in team settings. Vibe coding provides no support for cooperation between developers at the specification level, leaving them instead to rely on code reviews to resolve differences and conflicts in understanding the specifications.</p><p>If the developer must resort to code to fix the issue, most of the advantages of vibe coding disappear. First, the developer must be versed in the implementation technology. Second, to fix the issue without introducing new bugs the developer must understand the code base. Third, instead of doing the most valuable part of coding (implementing new functionality), the developer is left with the most tedious and least enjoyable task &#8212; fixing issues.</p><h2>Spec-Driven Development: The Mature Way Forward</h2><p>Vibe coding brings the efficiency of natural language to programming, but for control and precision, modular reuse, teamwork and clarity, <strong>developers still need to turn to code</strong>. To move beyond vibe coding and unlock the full potential of using natural language to specify software, developers must be able to <strong>fix any issue entirely within the specifications</strong> &#8212; without ever touching the underlying code. Spec-driven development delivers on this promise by making a key shift: instead of treating code as the source of truth &#8212; as is the case with task-oriented vibe coding &#8212; <strong>spec-driven development uses specifications as the source of truth for the software&#8217;s functionality</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>.</p><p>This represents a major paradigm shift in programming, and understandably, developers are very skeptical about whether it&#8217;s even possible. The two objections most frequently raised against using specifications as the source of truth are the inherent ambiguity of natural language and functionality flickering<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>.</p><p>Source code written in a programming language does not exhibit ambiguity; there is only one valid interpretation of a C program or a Python script. By contrast, <strong>natural language is inherently ambiguous</strong>, and there is always a possibility that someone will misinterpret a statement. But in the vast majority of cases, this is not a problem &#8212; humans understand each other quite well.</p><p>The solution to the problem of ambiguity in natural language is well established: when a reasonable person might perceive ambiguity, the specification should be strengthened to eliminate it. Humans have refined this for at least 4,000 years<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a> in codified legal systems &#8212; through interpretation and precedent &#8212; where it&#8217;s proven durable and effective.</p><p>In spec-driven development, ambiguity first needs to be detected &#8212; either through automated detection or by a developer noticing that the software is not behaving as intended. Once identified, the developer can respond in one of two ways: (i) by refining the specification to eliminate the ambiguity, or (ii) adding a special case to define how the software should handle the particular situation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HShG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HShG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 424w, https://substackcdn.com/image/fetch/$s_!HShG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 848w, https://substackcdn.com/image/fetch/$s_!HShG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 1272w, https://substackcdn.com/image/fetch/$s_!HShG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HShG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png" width="1456" height="901" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:901,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87072,&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://blog.codeplain.ai/i/172083270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.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_!HShG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 424w, https://substackcdn.com/image/fetch/$s_!HShG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 848w, https://substackcdn.com/image/fetch/$s_!HShG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 1272w, https://substackcdn.com/image/fetch/$s_!HShG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7aad5133-aa95-4614-bfc2-3accb9ae2ecf_2259x1398.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">Spec-driven development workflow for ambiguity resolution.</figcaption></figure></div><p>The other argument against using specifications as the source of truth is <strong>functionality flickering</strong>, that is, an undesirable phenomenon in spec-driven software development where <strong>underspecification</strong> makes generated functionality vary between code generations. For example, if the color of a button is left undefined, one generation might produce a green button and the next a red one, confusing users and undermining their trust.</p><p>Functionality flickering is not a new phenomenon &#8212; it only became noticeable with the rise of AI-powered code generation, which can produce multiple implementations from the same specification quickly and at low cost. The same effect appears with human developers: when given identical specifications, the end result will also look and feel very different. This variation arises because developers make many <strong>micro-decisions</strong> during coding, each influencing the functionality of the software. For example, if the color of a button is unspecified, one developer may choose yellow while another selects blue. From a functionality standpoint, the color is irrelevant (and therefore left unspecified). From a user&#8217;s perspective, however, the inconsistency can be confusing &#8212; for instance, if one version of the software presents an orange button while another shows a gray one<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>.</p><p>To prevent functionality flickering, it is necessary to detect and record the micro-decisions made during coding that affect software behavior. In subsequent code generations, these records must be reconsidered to ensure the software retains exactly the same functionality as before.</p><h2>Introducing ***plain, the Language of Spec-Driven Development</h2><p>To realize the vision of spec-driven development, *codeplain today announces <strong>***plain &#8212; a specification language that combines the efficiency of natural language with the control and precision of code</strong>. Built on Markdown, ***plain introduces new syntax that allows developers to express intent at any level of detail. This enables ***plain renderer to consistently and reliably generate software code from specifications.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ViJ7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ViJ7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 424w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 848w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 1272w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ViJ7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png" width="1456" height="428" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/482d01c0-2627-4582-86ad-17424c895f30_2151x633.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36148,&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://blog.codeplain.ai/i/172083270?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.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_!ViJ7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 424w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 848w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 1272w, https://substackcdn.com/image/fetch/$s_!ViJ7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F482d01c0-2627-4582-86ad-17424c895f30_2151x633.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div 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">***plain specification language logo</figcaption></figure></div><p>The core idea behind ***plain is that software can be specified as a union of (partially) overlapping functionalities, from which implementation code can always be generated from scratch. Developers are free to describe functionality in any way they choose, including using the full expressiveness of natural language, with the only limitation being that the description must be text-based. The functionalities can share concepts, thus enabling the building of complex functionality out of simpler functionalities.</p><p>In ***plain, specifications for overlapping functionalities must not contradict one another. If a conflict is found, it must be resolved by rewriting the specifications. Additionally, any complex functionality must be decomposed into simpler, more manageable parts. If decomposition is not possible, concrete examples must be provided in the form of acceptance tests<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a>. These examples help the ***plain renderer generate code in small, testable increments, ensuring consistent and reliable output.</p><p>To ensure that the implementation code fully conforms to the specification, unit tests and conformance tests<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-9" href="#footnote-9" target="_self">9</a> can be automatically generated from the same source specification. The specification can be further strengthened by additional acceptance tests written by the developer to disambiguate unclear specifications or address bugs in the implementation. Before new functionality is rendered or existing functionality is re-rendered, all tests must pass. Once functionality is implemented, the tests serve to detect regressions and verify that the implementation conforms to the specification.</p><p>Because ***plain specification is a collection of text files, all the powerful tooling that has evolved for coding can be fully reused. ***plain specification:</p><ul><li><p>Can be version controlled, enabling traceability and compliance</p></li><li><p>Can use templates, enabling modular reuse of specifications</p></li><li><p>Can be developed with AI-powered tools (e.g., Cursor)</p></li><li><p>Enables concurrent teamwork with seamless merging</p></li></ul><p>***plain also makes it easier for teams to share effective practices and successful patterns, much like they would with classical code. By expressing specifications in a standardized, structured form, developers can learn from one another&#8217;s approaches &#8212; for example, how to write clearer specifications or organize them into a coherent whole &#8212; without depending on informal, ad-hoc knowledge transfer.</p><p>The structure of the ***plain specification language &#8212; especially its syntax &#8212; is still under active development. Its first official release is planned for fall 2025, guided by the ***plain language working group.</p><p>We are now opening the group to a limited number of new members. The group&#8217;s initial members include <strong>Dusan Omercevic</strong>, founder of *codeplain, and <strong>Johan Rosenkilde</strong>, creator of SpecLang<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-10" href="#footnote-10" target="_self">10</a> and a former member of the original GitHub Copilot team.</p><p>If you&#8217;re interested in contributing, please reach out to us at <a href="mailto:lang@plainlang.org">lang@plainlang.org</a>.</p><div><hr></div><p><em>The development of ***plain is made possible through the support of *codeplain, an AI-powered code generation service that produces production-ready software from specifications written in the ***plain language. For more information, visit <a href="http://www.codeplain.ai">www.codeplain.ai</a> or email <a href="mailto:info@codeplain.ai">info@codeplain.ai</a>.</em></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.codeplain.ai/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"><strong>Thanks for reading *codeplain!</strong> Subscribe for free to receive monthly blog posts.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><a href="https://x.com/karpathy/status/1886192184808149383">https://x.com/karpathy/status/1886192184808149383</a>,</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><a href="https://github.com/githubnext/eea/blob/main/docs/report.md#problems-ambiguity-and-instability">https://github.com/githubnext/eea/blob/main/docs/report.md#problems-ambiguity-and-instability</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><a href="https://www.reddit.com/r/SaaS/comments/1lp3scg/i_am_giving_up/">https://www.reddit.com/r/SaaS/comments/1lp3scg/i_am_giving_up/</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Hat tip to John Berryman for framing the move from vibe coding to spec-driven development as &#8216;the mature way forward&#8217; (personal communication).</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p><a href="https://github.com/githubnext/eea/blob/main/docs/report.md#problems-ambiguity-and-instability">https://github.com/githubnext/eea/blob/main/docs/report.md#problems-ambiguity-and-instability</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p><a href="https://en.wikipedia.org/wiki/Code_of_Hammurabi">https://en.wikipedia.org/wiki/Code_of_Hammurabi</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p>Please note that we are intentionally inconsistent with colors in this paragraph to illustrate the flickering phenomenon being described.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p>Acceptance tests verify requirements and validate user needs.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-9" href="#footnote-anchor-9" class="footnote-number" contenteditable="false" target="_self">9</a><div class="footnote-content"><p>Conformance tests verify correctness against specifications.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-10" href="#footnote-anchor-10" class="footnote-number" contenteditable="false" target="_self">10</a><div class="footnote-content"><p>SpecLang was an experimental project at GitHub Next, an early attempt at spec-driven development that explored using structured natural language to generate code. See <a href="https://githubnext.com/projects/speclang/">https://githubnext.com/projects/speclang/</a> for more information about the SpecLang project.</p></div></div>]]></content:encoded></item></channel></rss>